我想对使用 Jetpack Compose 的 Activity 进行 UI 测试。 docs 提供了一些有关如何使用两种变体测试此类屏幕的信息:
@get:Rule val composeTestRule = createComposeRule()
如果我不需要活动本身运行并且只想测试我的可组合项或
@get:Rule val composeTestRule = createAndroidComposeRule<MyActivity>()
如果我确实需要这项活动。
在第二种情况下,如何将带有 Extras 的 Intent 传递给 Activity?
我已经尝试过:
@Before
fun setUp() {
composeTestRule.activity.intent = Intent().apply {
putExtra(
"someKey",
123
)
}
}
但是活动中的意图额外内容仍然为空。
在
composeTestRule.activity.intent
中设置 setUp()
的问题是 Activity 已在此时创建,并且 Activity 的 OnCreate
已被调用。因此,您在 setUp()
中设置的意图属性正在设置,但在 Activity.OnCreate
中使用已经太晚了。
不幸的是,Google 还没有像
createAndroidComposeRule<MyActivity>()
那样创建辅助方法。不过,可以编写一个辅助方法来解决:
class MyActivityTest {
@get:Rule
val composeRule = createEmptyComposeRule()
@Test
fun firstTimeLogIn() = composeRule.launch<MyActivity>(
onBefore = {
// Set up things before the intent
},
intentFactory = {
Intent(it, MyActivity::class.java).apply {
putExtra("someKey", 123)
}
},
onAfterLaunched = {
// Assertions on the view
onNodeWithText("Username").assertIsDisplayed()
})
}
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()
}
@RunWith(AndroidJUnit4::class)
class MyActivityTest {
@get:Rule
val composeTestRule = createAndroidIntentComposeRule<MyActivity> {
Intent(it, MyActivity::class.java).apply {
putExtra("someKey", 123)
}
}
@Test
fun Test1() {
}
}
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!")
}
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
}
}
希望此代码片段对您有用
这是一个简单的解决方案:
@Before
fun setUp() {
composeTestRule.activity.intent.apply {
putExtra(
"someKey",
123
)
}
}