Можно ли шпионить за приостановленными Android функциями Room DAO с помощью MockK - PullRequest
2 голосов
/ 01 августа 2020

Я исследую библиотеку MockK с помощью моих Android тестов JUnit

testImplementation "io.mockk:mockk:1.10.0"

У меня проблема при попытке слежения за функциями приостановки

вот мой тест Junit

@ExperimentalCoroutinesApi
@FlowPreview
@RunWith(AndroidJUnit4::class)
class BackOffCriteriaDaoTest : BaseTest() {

    @Rule
    @JvmField
    val instantTaskExecutorRule = InstantTaskExecutorRule()

    private lateinit var dao: BackoffCriteriaDAO

        @Test
        fun backOffCriteria() = runBlocking {
            dao = spyk(myRoomDatabase.backoffCriteriaDAO())
            assertNotNull(dao.getBackoffCriteria())
            assertEquals(backOffCriteriaDO, dao.getBackoffCriteria())
            dao.delete()
    
            coVerify {
                myRoomDatabase.backoffCriteriaDAO()
                dao.reset()
            }
        }
    }

Этот тест вызывает ошибку java .lang.AssertionError в dao.reset() следующим образом: -

java.lang.AssertionError: Verification failed: call 2 of 2: BackoffCriteriaDAO_Impl(#2).reset(eq(continuation {}))). Only one matching call to BackoffCriteriaDAO_Impl(#2)/reset(Continuation) happened, but arguments are not matching:
[0]: argument: continuation {}, matcher: eq(continuation {}), result: -

Мой метод dao reset () выглядит следующим образом: -

@Transaction
suspend fun reset() {
    delete()
    insert(BackoffCriteriaDO(THE_BACKOFF_CRITERIA_ID, BACKOFF_CRITERIA_MILLISECOND_DELAY, BACKOFF_CRITERIA_MAX_RETRY_COUNT))
}

Почему я вижу это java.lang.AssertionError? Как мне вызвать coVerify функции приостановки?

ОБНОВЛЕНИЕ

Я считаю, что проблема вызвана тем, что я использую базу данных Room. Мой метод интерфейса dao reset() реализован кодом, сгенерированным комнатой, как

 @Override
  public Object reset(final Continuation<? super Unit> p0) {
    return RoomDatabaseKt.withTransaction(__db, new Function1<Continuation<? super Unit>, Object>() {
      @Override
      public Object invoke(Continuation<? super Unit> __cont) {
        return BackoffCriteriaDAO.DefaultImpls.reset(BackoffCriteriaDAO_Impl.this, __cont);
      }
    }, p0);
  }

, что означает, что coVerify {} соответствует этой функции, а не моей версии интерфейса.

Возможно ли сопоставление эта сгенерированная версия public Object reset(final Continuation<? super Unit> p0)?

Является ли это более простой проблемой c с mockk, которая не может имитировать классы java? Или Java реализации Kotlin интерфейсов?

ОБНОВЛЕНИЕ 2

Когда мои функции Room DAO не приостанавливаются, Mockk работает как требуется

используя эти фиктивные функции в моем DAO: -

@Transaction
fun experimentation() {
    experiment()
}

@Transaction
fun experiment() {
    experimental()
}

@Query("DELETE from backoff_criteria")
fun experimental()

Мой тест проходит

@Test
fun experimentation() = runBlocking {
    val actual = myRoomDatabase.backoffCriteriaDAO()
    val dao = spyk(actual)

    dao.experimentation()

    verify { dao.experiment() }
}

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

@Transaction
suspend fun experimentation() {
    experiment()
}

@Transaction
fun experiment() {
    experimental()
}

@Query("DELETE from backoff_criteria")
fun experimental()

Однако, когда я меняю свои фиктивные функции следующим образом, тест вызывает исключение

@Transaction
suspend fun experimentation() {
    experiment()
}

@Transaction
suspend fun experiment() {
    experimental()
}

@Query("DELETE from backoff_criteria")
fun experimental()

Неудачные тесты выглядят следующим образом: -

@Test
fun experimentation() = runBlocking {
    val actual = myRoomDatabase.backoffCriteriaDAO()
    val dao = spyk(actual)

    dao.experimentation()

    coVerify { dao.experiment() }

}

Исключение составляет

java.lang.AssertionError: Verification failed: call 1 of 1: BackoffCriteriaDAO_Impl(#2).experiment(eq(continuation {}))). Only one matching call to BackoffCriteriaDAO_Impl(#2)/experiment(Continuation) happened, but arguments are not matching:
[0]: argument: continuation {}, matcher: eq(continuation {}), result: -

1 Ответ

2 голосов
/ 04 августа 2020

Возможно, в spy нет ничего плохого, но в асинхронном характере вызываемой вами функции транзакции.

Для тестирования функций приостановки с областью видимости вам может потребоваться использовать

launch Builder и время продвижения до простоя или в течение периода времени для проверки хода выполнения, как это сделано с Rx Java счетчиками.

У меня была такая же проблема с MockWebServer, здесь вы можете проверить question .

  launch {
         dao.delete()
    }

    advanceUntilIdle()

И используйте правило Coroutine с тестами, чтобы иметь одинаковую область действия для каждой операции.

class TestCoroutineRule : TestRule {

    private val testCoroutineDispatcher = TestCoroutineDispatcher()

    val testCoroutineScope = TestCoroutineScope(testCoroutineDispatcher)

    override fun apply(base: Statement, description: Description?) = object : Statement() {

        @Throws(Throwable::class)
        override fun evaluate() {

            Dispatchers.setMain(testCoroutineDispatcher)

            base.evaluate()

            Dispatchers.resetMain()
            try {
                testCoroutineScope.cleanupTestCoroutines()
            } catch (exception: Exception) {
                exception.printStackTrace()
            }
        }
    }

    fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
        testCoroutineScope.runBlockingTest { block() }

}

Вы можете использовать правило, как показано ниже

 testCoroutineRule.runBlockingTest {

           dao.delete()

           advanceUntilIdle()
           
            coVerify {
                myRoomDatabase.backoffCriteriaDAO()
                dao.reset()
            }

        }

Также вы можете попробовать поместить dao.delete () в launch. В некоторых тестах он не работал без запуска, в то время как другие работали без него, и даже некоторые из них нестабильны со всем, что я пробовал. Необходимо решить некоторые проблемы с тестовыми сопрограммами.

здесь вы можете проверить, как это делается, и есть некоторые проблемы с тестовыми сопрограммами, вы можете проверить мой другой вопрос здесь .

Я создал игровую площадку для тестирования сопрограмм, это может быть полезно, и вы можете проверить проблемы с сопрограммами, а еще один с mockK и тесты сопрограмм.

...