'Add intent extras in compose UI test
I want to UI test an Activity that uses Jetpack Compose. The docs provide some information on how to test such a screen with two variants:
@get:Rule val composeTestRule = createComposeRule()
if I don't need the activity itself to run and want to test just my composables or
@get:Rule val composeTestRule = createAndroidComposeRule<MyActivity>()
if I do need the activity.
In the second case, how can I pass an Intent with Extras to the activity?
I've tried:
@Before
fun setUp() {
composeTestRule.activity.intent = Intent().apply {
putExtra(
"someKey",
123
)
}
}
but the intent extras are still null in the activity.
Solution 1:[1]
The issue with setting composeTestRule.activity.intent in setUp() is that the Activity is already created at that point and the Activity's OnCreate was already called. So your intent properties that your setting in setUp() are being set but it's too late to be consumed in Activity.OnCreate.
Unfortunately Google doesn't create a helper method like they do with createAndroidComposeRule<MyActivity>(), yet. However it's possible to write a helper method to workaround:
Option 1 (An intent per test)
How to use
class MyActivityTest {
@get:Rule
val composeRule = createEmptyComposeRule()
@Test
fun firstTimeLogIn() = composeRule.launch<MyActivity>(
onBefore = {
// Setup things before the intent
},
intentFactory = {
Intent(it, MyActivity::class.java).apply {
putExtra("someKey", 123)
}
},
onAfterLaunched = {
// Assertions on the view
onNodeWithText("Username").assertIsDisplayed()
})
}
Kotlin Extension Helper Method
import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.junit4.createEmptyComposeRule
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
/**
* Uses a [ComposeTestRule] created via [createEmptyComposeRule] that allows setup before the activity
* is launched via [onBefore]. Assertions on the view can be made in [onAfterLaunched].
*/
inline fun <reified A: Activity> ComposeTestRule.launch(
onBefore: () -> Unit = {},
intentFactory: (Context) -> Intent = { Intent(ApplicationProvider.getApplicationContext(), A::class.java) },
onAfterLaunched: ComposeTestRule.() -> Unit
) {
onBefore()
val context = ApplicationProvider.getApplicationContext<Context>()
ActivityScenario.launch<A>(intentFactory(context))
onAfterLaunched()
}
Option 2 (Single intent for each test)
How to use
@RunWith(AndroidJUnit4::class)
class MyActivityTest {
@get:Rule
val composeTestRule = createAndroidIntentComposeRule<MyActivity> {
Intent(it, MyActivity::class.java).apply {
putExtra("someKey", 123)
}
}
@Test
fun Test1() {
}
}
Kotlin Extension Helper Method
import android.content.Context
import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.rules.ActivityScenarioRule
/**
* Factory method to provide android specific implementation of createComposeRule, for a given
* activity class type A that needs to be launched via an intent.
*
* @param intentFactory A lambda that provides a Context that can used to create an intent. A intent needs to be returned.
*/
inline fun <A: ComponentActivity> createAndroidIntentComposeRule(intentFactory: (context: Context) -> Intent) : AndroidComposeTestRule<ActivityScenarioRule<A>, A> {
val context = ApplicationProvider.getApplicationContext<Context>()
val intent = intentFactory(context)
return AndroidComposeTestRule(
activityRule = ActivityScenarioRule(intent),
activityProvider = { scenarioRule -> scenarioRule.getActivity() }
)
}
/**
* Gets the activity from a scenarioRule.
*
* https://androidx.tech/artifacts/compose.ui/ui-test-junit4/1.0.0-alpha11-source/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt.html
*/
fun <A : ComponentActivity> ActivityScenarioRule<A>.getActivity(): A {
var activity: A? = null
scenario.onActivity { activity = it }
return activity ?: throw IllegalStateException("Activity was not set in the ActivityScenarioRule!")
}
Solution 2:[2]
class ActivityTest {
private lateinit var scenario: ActivityScenario<Activity>
@get:Rule
val composeAndroidRule = createEmptyComposeRule()
@Before
fun setUp() {
scenario = ActivityScenario.launch(
createActivityIntent(
InstrumentationRegistry.getInstrumentation().targetContext,
)
)
}
private fun createActivityIntent(
context: Context
): Intent {
val intent = Intent(context, Activity::class.java)
return intent
}
}
hopefully, this code snippet would works for you
Solution 3:[3]
Try to set the parameter name correctly, for example :
putExtra(Intent.EXTRA_TEXT, "This is my text to send.")
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Oscar Ivan |
| Solution 3 | Stefano Sansone |
