【问题标题】:Making a CoroutineDispatcher IdlingResource for Espresso Tests为 Espresso 测试制作 CoroutineDispatcher IdlingResource
【发布时间】:2019-06-17 11:46:37
【问题描述】:

我正在尝试找到一种方法来很好地实现 IdlingResource,它将轮询 CoroutineDispatcher 的 isActive 属性。但是,从调试来看,检查此属性时似乎从来没有活动的作业。

到目前为止,我已经尝试使用 AsyncTask 的 THREAD_POOL_EXECUTOR 进行内置空闲,但在使用 asCoroutineDispatcher 扩展函数并使用生成的 CoroutineDispatcher 来启动我的 ViewModel 的工作时,它似乎不起作用。我尝试编写自定义 IdlingResource

视图模型

fun authenticate(username: String, password: String) = viewModelScope.launch(Dispatchers.Default) {
    if (_authenticateRequest.value == true) {
        return@launch
    }

    _authenticateRequest.postValue(true)
    val res = loginRepo.authenticate(username, password)
    _authenticateRequest.postValue(false)

    when {
        res is Result.Success -> {
            _authenticateSuccess.postValue(res.item)
        }
        res is Result.Failure && res.statusCode.isHttpClientError -> {

            _authenticateFailure.postValue(R.string.invalid_password)
        }
        else -> {
            _authenticateFailure.postValue(R.string.network_error)
        }
    }
}

空闲资源

class CoroutineDispatcherIdlingResource(
    private val resourceName: String,
    private val dispatcher: CoroutineDispatcher
) : IdlingResource {
    private var callback: IdlingResource.ResourceCallback? = null

    override fun getName() = resourceName

    override fun isIdleNow(): Boolean {
        if (dispatcher.isActive) { return false }

        callback?.onTransitionToIdle()
        return true
    }

    override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
        this.callback = callback
    }
}

浓缩咖啡测试

@RunWith(AndroidJUnit4::class)
class LoginIntegrationTest {
    @get:Rule
    val activityRule = ActivityTestRule(MainActivity::class.java)

    var idlingResource: CoroutineDispatcherIdlingResource? = null

    @Before
    fun before() {
        idlingResource = CoroutineDispatcherIdlingResource(this.javaClass.simpleName, Dispatchers.Default)
        IdlingRegistry.getInstance().register(idlingResource)
    }

    @Test
    fun loginFailure() {
        onView(withId(R.id.username))
            .perform(clearText()).perform(typeText("aslkdjqwe"))
        onView(withId(R.id.password))
            .perform(clearText()).perform(typeText("oxicjqwel"))
        onView(withId(R.id.login_button))
            .perform(click())

        onView(withId(com.google.android.material.R.id.snackbar_text))
            .check(matches(withText(R.string.invalid_password)))
    }
}

一旦调用 ViewModel 'authenticate' 函数,我希望 isActive 属性为真,但情况似乎并非如此。它总是看起来是错误的,因为 CoroutineDispatcher 中从来没有一个活动的 Job。

【问题讨论】:

    标签: android kotlin android-espresso kotlin-coroutines


    【解决方案1】:

    想出了一个解决办法!事实证明,AsyncTask 的 THREAD_POOL_EXECUTOR 实际上可以正常工作。我缺少的是用于 Retrofit/OkHttp 的 IdlingResource。

    我最初的假设是在 THREAD_POOL_EXECUTOR 上运行的协程会在 HTTP 客户端关闭时隐式等待,但我使用 IdlingResource here 来很好地完成所有事情。

    【讨论】:

      【解决方案2】:

      CoroutineContext.isActive 具有误导性,因为它会检查上下文是否有 Job 对象以及它是否处于活动状态。 CoroutineDispatcher 是一个没有像 Job 这样的其他元素的上下文,所以它总是返回 false

      为了跟踪延续,您可能需要某种自定义 ContinuationInterceptor 来跟踪正在进行和取消的延续。

      【讨论】:

      • 感谢您的建议。我有这个想法,所以这将是我接下来要走的路。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-19
      相关资源
      最近更新 更多