Подпрограммы модульного тестирования runBlockingTest: эта работа еще не завершена - PullRequest
1 голос
/ 15 апреля 2020

Ниже приведена функция, использующая сопрограмму для замены обратного вызова:

override suspend fun signUp(authentication: Authentication): AuthenticationError {
    return suspendCancellableCoroutine {
        auth.createUserWithEmailAndPassword(authentication.email, authentication.password)
            .addOnCompleteListener(activityLifeCycleService.getActivity()) { task ->
                if (task.isSuccessful) {
                    it.resume(AuthenticationError.SignUpSuccess)
                } else {
                    Log.w(this.javaClass.name, "createUserWithEmail:failure", task.exception)
                    it.resume(AuthenticationError.SignUpFail)
                }
            }
    }
}

Теперь я хотел бы провести модульное тестирование этой функции. Я использую Mockk:

  @Test
  fun `signup() must be delegated to createUserWithEmailAndPassword()`() = runBlockingTest {

      val listener = slot<OnCompleteListener<AuthResult>>()
      val authentication = mockk<Authentication> {
        every { email } returns "email"
        every { password } returns "pswd"
      }
      val task = mockk<Task<AuthResult>> {
        every { isSuccessful } returns true
      }

      every { auth.createUserWithEmailAndPassword("email", "pswd") } returns
          mockk {
            every { addOnCompleteListener(activity, capture(listener)) } returns mockk()
          }

    service.signUp(authentication)

      listener.captured.onComplete(task)
    }

К сожалению, этот тест не прошел из-за следующего исключения: java.lang.IllegalStateException: This job has not completed yet

Я попытался заменить runBlockingTest на runBlocking, но тест, похоже, ждет в бесконечном l oop.

Может кто-нибудь помочь мне с этим UT, пожалуйста?

Заранее спасибо

1 Ответ

0 голосов
/ 23 апреля 2020

Как видно из этого сообщения:

Это исключение обычно означает, что некоторые сопрограммы из ваших тестов были запланированы вне области тестирования (более конкретно, диспетчер тестов).

Вместо выполнения этого:

private val networkContext: CoroutineContext = TestCoroutineDispatcher()

private val sut = Foo(
  networkContext,
  someInteractor
)

fun `some test`() = runBlockingTest() {
  // given
  ...

  // when
  sut.foo()

  // then
  ...
}

Создайте тестовую область, проходящую тестовый диспетчер:

private val testDispatcher = TestCoroutineDispatcher()
private val testScope = TestCoroutineScope(testDispatcher)
private val networkContext: CoroutineContext = testDispatcher

private val sut = Foo(
  networkContext,
  someInteractor
)

Затем в тесте выполните testScope.runBlockingTest

fun `some test`() = testScope.runBlockingTest {
  ...
}

См. Также Крейг Рассел «Юнит-тестирование функций приостановки подпрограммы с использованием TestCoroutineDispatcher»

...