android / android/android-test

No way to test "System needs resources" situation using `ActivityScenario`

Open
#1,825 2 comments 3 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
1.2k
Forks
342
Avg merge
11h 29m
Merged PRs (30d)
2

Description

### Description

I'll refer to the lingo used in the recent Google I/O video [Best practices for saving UI state on Android](https://www.youtube.com/watch?v=V-s4z7B_Gnc) as that coincidently covered the situation I'm describing here. It lays out three ways of "losing app state":

1. Configuration changes
2. System needs resources
3. App dismissal

Using `ActivityScenario` it's currently possible to write tests for **1** and **3**, but not for **2**.

Here's an example of testing **1**:

```kotlin
val activityScenario = ActivityScenario.launch(Activity::class.java)

// Do something

activityScenario.recreate() // Simulates a config change

// Check something
```

...and here's an example of testing **3**:
```kotlin
val activityScenario1 = ActivityScenario.launch(Activity::class.java)

// Do something

val activityScenario2 = ActivityScenario.launch(Activity::class.java) // Simulates a dismissal and a reopen

// Check something
```

As far as I understand, when **2** occurs, the following things happen:

1. `onSaveInstanceState` is called on the Activity
2. The Activity is destroyed
3. The process is killed

When the user navigates back after **2** the Activity is created again with the `Bundle` earlier created by `onSavedInstanceState`. The Activity and its Fragments (or potentially a whole back stack) is still being "recreated" his is distinct from **1** in that the process (and any state it owns) is being destroyed (meaning loss of Application level, ViewModel or any unfortunately static state). As an aside, a good way to reproduce this situation locally (without changing background limits or using a low memory phone) is to remove a permission from an app in system settings.

We actually can test this scenario using Robolectric in a local test:

```kotlin
val activityController1 = Robolectric.buildActivity(Activity::class.java)
activityController1.setup()

// Do something

val outState = Bundle()
activityController1
.pause()
.saveInstanceState(outState)
.stop()
.destroy()
.close()

val activityController2 = Robolectric.buildActivity(Activity::class.java)
activityController2.setup(outState)

// Check something
```

Given this, it seems like it'd be possible to add support for this situation to `LocalActivityInvoker` and build an API around it for `ActivityScenario`, but I'm not sure how plausible it is for `InstrumentationActivityInvoker` which would be good to discuss here. It could be that this isn't something that AndroidX Test will ever support and will forever be left up to "local" tests, but if so that would be good to call out so that Robolectric support can be documented and/or smoothed out.

### AndroidX Test and Android OS Versions

AndroidX Test 1.5.0, any version of Android

Contributor guide

Open the contributing guide

Research direction

Start by reading ActivityScenario and the LocalActivityInvoker and InstrumentationActivityInvoker paths mentioned in the issue, then compare them with the Robolectric lifecycle sequence using Bundle state. Determine whether the system-needs-resources scenario can be supported by both invokers. Done means the capability is implemented and testable, or the supported Robolectric-only limitation is documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, java, kotlin
Domain
mobile, testing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.