android-test
android-test copied to clipboard
finish() from onStop() doesn't set ActivityScenario's state to DESTROYED
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
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.
Is it possible onActivity
might be synchronizing before executing the lambda, but not after?
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!
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
).
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()
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!