文档在测试 Fragments 方面非常好,但没有关于如何测试使用 ActivityResult 的 Activity 的信息。
activityResultRegistry我们应该如何在 Activity 测试中覆盖?
文档在测试 Fragments 方面非常好,但没有关于如何测试使用 ActivityResult 的 Activity 的信息。
activityResultRegistry我们应该如何在 Activity 测试中覆盖?
将你的合约写在一个单独的文件中,这样你就可以轻松地测试合约并在运行时提供你自己的 ActivityResultRegistry 来伪造预期的结果。实际上调用真正的合约来测试活动是不好的做法。设计合约的核心目的之一是将活动代码与 onActicityResults 分离
class ImageContract(registry: ActivityResultRegistry) {
private val contractUriResult : MutableLiveData<Uri> = MutableLiveData(null)
private val getPermission = registry.register(REGISTRY_KEY, ActivityResultContracts.GetContent()) { uri ->
contractUriResult.value = uri
}
fun getImageFromGallery(): LiveData<Uri> {
getPermission.launch("image/*")
return contractUriResult
}
companion object {
private const val REGISTRY_KEY = "Image Picker"
}
}
在你的活动中
ImageContractHandler(activityResultRegistry).getImageFromGallery().observe(this, {
it?.let { u ->
backgroundImageView.setImageURI(u)
}
})
在您的测试中
@Test
fun activityResultTest() {
// Create an expected result URI
val testUrl = "file//dummy_file.test"
val expectedResult = Uri.parse(testUrl)
// Create the test ActivityResultRegistry
val testRegistry = object : ActivityResultRegistry() {
override fun <I, O> onLaunch(
requestCode: Int,
contract: ActivityResultContract<I, O>,
input: I,
options: ActivityOptionsCompat?
) {
dispatchResult(requestCode, expectedResult)
}
}
val uri = ImageContractHandler(testRegistry).getImageFromGallery().getOrAwaitValue()
assert(uri == expectedResult)
}
为了在 Tests 中的同一线程上侦听 LiveData,有一个著名的 livedata 测试扩展
fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS,
afterObserve: () -> Unit = {}
): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
this@getOrAwaitValue.removeObserver(this)
}
}
this.observeForever(observer)
afterObserve.invoke()
// Don't wait indefinitely if the LiveData is not set.
if (!latch.await(time, timeUnit)) {
this.removeObserver(observer)
throw TimeoutException("LiveData value was never set.")
}
@Suppress("UNCHECKED_CAST")
return data as T
}
我没有像我应该的那样准确地阅读文档。
注意:任何允许您
ActivityResultRegistry在测试中注入单独的机制都足以启用测试您的活动结果调用。
强调注入这个词。
我在我的项目中使用 Koin,所以我决定使用 Scopes api 创建一个 Activity Scoped 实例ActivityResultRegistry,我将它注入到我的registerForActivityResult-call 中。
val activityScopeModule = module {
scope<MyActivity> {
scoped { get<ComponentActivity>().activityResultRegistry }
}
}
class MyActivity: AppCompatActivity() {
private val requestPermLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission(),
get<ActivityResultRegistry>() // Koin injection
) { granted ->
// handle
}
}
通过使用 DI,在测试中注入我的自定义测试实例ActivityResultRegistry变得非常容易。
关于该主题的有用博客文章(使用 Hilt 来完成相同的任务):https ://blog.stylingandroid.com/activity-result-contract-outside-the-activity/
关于 Koin Scopes API 的博客文章:https ://proandroiddev.com/understanding-android-scopes-with-koin-cfe6b60ca579