android-test icon indicating copy to clipboard operation
android-test copied to clipboard

finish() from onStop() doesn't set ActivityScenario's state to DESTROYED

Open 0xDCA opened this issue 3 years ago • 6 comments

Description

public class FinishOnStopActivity extends Activity {
  @Override
  protected void onStop() {
    super.onStop();
    finish();
  }
}
...
@Test
public void finishOnStopActivityShouldBeDestroyed() throws Exception {
  try (ActivityScenario<FinishOnStopActivity> scenario = ActivityScenario.launch(FinishOnStopActivity.class)) {
    // Call onStop()
    scenario.moveToState(State.CREATED);
    ShadowLooper.runUiThreadTasks();

    // This line succeeds, but the DESTROYED check fails.
    // scenario.onActivity(activity -> assertThat(activity.isFinishing()).isTrue());
    assertThat(scenario.getState()).isEqualTo(State.DESTROYED);
  }
}
...

With this test, the State is actually CREATED even though the activity is marked as finishing.

Expected Results

The test should pass, since the activity is being destroyed.

Actual Results

The test doesn't pass, the state remains CREATED.

AndroidX Test and Android OS Versions

1.4.0-beta02 I tested on Robolectric

0xDCA avatar Jun 07 '21 19:06 0xDCA

It seems to be generally true that calling finish() doesn't move the state to DESTROYED. A minimal test that demonstrates this is:

@Test
fun finish_moves_to_destroyed_state() {
    val scenario = ActivityScenario.launch(SomeActivity::class.java)
    scenario.onActivity(SomeActivity::finish)
    assertThat(scenario.state, equalTo(Lifecycle.State.DESTROYED))
}

This fails in both test and androidTest (so with both Robolectric and instrumented/on-device tests) as the state is RESUMED rather than DESTROYED. Interestingly (and as a possible workaround) scenarion.onActivity { assertThat(it.finishing, equalTo(true)) } works here. I got this using Robolectric 4.5.1 and AndroidX Test 1.4.0.

seadowg avatar Jan 10 '22 12:01 seadowg

Is it possible onActivity might be synchronizing before executing the lambda, but not after?

TWiStErRob avatar Jan 14 '22 19:01 TWiStErRob

Is it possible onActivity might be synchronizing before executing the lambda, but not after?

Sounds plausible. I tried adding shadowOf(getMainLooper()).idle(); after the onActivity call and that didn't help. Feels like it'd worth investigating down that avenue though!

seadowg avatar Jan 17 '22 10:01 seadowg

An update: I noticed that I was able to do assertThat(scenario.state, equalTo(Lifecycle.State.DESTROYED)) successfully (after forgetting I'd run into problems with it earlier) for an Activity that called finish as part of its onCreate (not important but might be helpful for illustrative purposes: it does so when it receives invalid extras).

As a super simple example, if I have an Activity like this:

class MyActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        finish()
    }
}

Then the following test passes:

val scenario = ActivityScenario.launch<MyActivity>(intent)
assertThat(scenario.state, equalTo(Lifecycle.State.DESTROYED))

This is in contract to my earlier example which calls finish as part of the test and the original example in the issue description where finish is called due to lifecycle being manipulated (again in the test). This suggests that finish is working correctly, but not when called directly, or indirectly from the test. I'm not quite sure why it works in onCreate though (as you could argue that's being called "indirectly" from launch).

seadowg avatar Apr 15 '22 13:04 seadowg

Ah, that's interesting. Thanks for looking into this.

Do you have any ideas of why the similar case posted in the OP didn't work? It is the same as what you do, except that finish() is called from onStop()

0xDCA avatar Apr 15 '22 14:04 0xDCA

It is the same as what you do, except that finish() is called from onStop()

Yeah, that's what I meant by:

and the original example in the issue description where finish is called due to lifecycle being manipulated (again in the test)

I didn't get a chance to try out an example like yours using moveToState in the test, but it feels like that fits the theory so far!

seadowg avatar Apr 15 '22 14:04 seadowg