【问题标题】:How to test an Activity that uses ActivityResult APIs?如何测试使用 ActivityResult API 的 Activity?
【发布时间】:2021-05-30 20:45:25
【问题描述】:

docs 在测试 Fragments 方面非常出色,但没有关于如何测试使用 ActivityResult 的 Activity 的信息。

我们应该如何在活动测试中覆盖activityResultRegistry

【问题讨论】:

    标签: android android-activity android-testing android-instrumentation registerforactivityresult


    【解决方案1】:

    将您的合约写在一个单独的文件中,这样您就可以轻松地测试合约并在运行时提供您自己的 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
    }
    

    【讨论】:

      【解决方案2】:

      我没有按照我应该阅读的那样准确地阅读文档。

      注意:任何允许您在测试中注入单独的ActivityResultRegistry 的机制都足以支持测试您的活动结果调用。

      强调注入这个词。

      我在我的项目中使用 Koin,因此我决定使用 Scopes api 创建一个 ActivityResultRegistry 的 Activity Scoped 实例,我将其注入到我的 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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-10
        • 1970-01-01
        • 1970-01-01
        • 2013-10-30
        • 1970-01-01
        • 2012-03-19
        • 1970-01-01
        • 2016-07-19
        相关资源
        最近更新 更多