Разрыв проверки класса MockK с помощью Dagger - PullRequest
1 голос
/ 18 февраля 2020

Обзор

Ожидается : при тестировании Dagger создан класс репозитория с пустым конструктором

Проблема : проверка Mockk не работает с mockkConstructor и confirmVerified с использованием внедрения зависимости Dagger для создания класса репозитория.

При реализации репозитория с шаблоном Dagger Android требуется пустой constructor с @Inject для того, чтобы компонент приложения Dagger, AppComponent.kt , для создания класса.

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

Работа в соответствии с ожиданиями - хранилище Singleton без создания Dagger

В модульном тесте mockkObject используется для макета SomeRepository.kt объекта, потому что это объект, созданный вручную в Kotlin.

SomeRepository.kt

object SomeRepository {
    someMethod(...){...}  
}

SomeParamUnitTest.kt

@ExtendWith(SomeTestExtension::class)
class SomeParamUnitTest(val testDispatcher: TestCoroutineDispatcher) {

    private fun TestLoad() = teedLoadTestCases()
    private lateinit var someViewModel: SomeViewModel

    @BeforeAll
    fun beforeAll() {
        mockkObject(SomeRepository)
        ...
    }

    @AfterAll
    fun afterAll() {
        unmockkAll()
    }

    @ParameterizedTest
    @MethodSource("TestLoad")
    fun `Feed Load`(test: FeedLoadTest) = testDispatcher.runBlockingTest {
        mockComponents(test)
        someViewModel = SomeViewModel(...)
        assertContentList(...) // Working as expected.
        verifyTests(test)
    }

    private fun mockComponents(test: FeedLoadTest) {
        ...
        coEvery {
            SomeRepository.someMethod(test.isRealtime, any())
        } returns mockSomeMethod(test.mockFeedList, test.status)
        ...
    }

    private fun verifyTests(test: FeedLoadTest) {
        coVerify {
            SomeRepository.someMethod(test.isRealtime, any())
        }
        confirmVerified(SomeRepository)
    }
}

Не работает - confirmVerified с введенным хранилищем Dagger

Вместо использования mockkObject для SomeRepository.kt в модульном тесте mockkConstructor реализовано в соответствии с документацией автора MockK, потому что в инжектируемом хранилище Dagger есть конструктор. Кроме того, anyConstructed используется для проверки методов SomeRepository.kt и прослушивания для проверки вызываемых методов.

Реализация

App. kt

class App : Application() {
    val appComponent = DaggerAppComponent.create()
    ...
}

AppComponent.kt

@Singleton
@Component
interface AppComponent {
    fun someRepository(): SomeRepository

    // The repository is injected inside of a fragment, then passed as a parameter into the ViewModel.
    fun inject(someFragment: SomeFragment)
}

SomeRepository.kt

@Singleton
class SomeRepository @Inject constructor() {
    someMethod(...){...}  
}

SomeParamUnitTest.kt

@ExtendWith(SomeTestExtension::class)
class SomeParamUnitTest(val testDispatcher: TestCoroutineDispatcher) {

    private fun TestLoad() = teedLoadTestCases()
    private lateinit var repository: SomeRepository
    private lateinit var someViewModel: SomeViewModel

    @BeforeAll
    fun beforeAll() {
        mockkConstructor(SomeRepository::class)
        ...
    }

    @AfterAll
    fun afterAll() {
        unmockkAll()
    }

    @ParameterizedTest
    @MethodSource("TestLoad")
    fun `Feed Load`(test: FeedLoadTest) = testDispatcher.runBlockingTest {
        repository = SomeRepository()
        mockComponents(test)
        someViewModel = SomeViewModel(...)
        assertContentList(...) // Working as expected.
        verifyTests(test)
    }

    private fun mockComponents(test: FeedLoadTest) {
        ...
        coEvery {
            anyConstructed<SomeRepository>().someMethod(test.isRealtime, any())
        } returns mockSomeMethod(test.mockFeedList, test.status)
        ...
    }

    private fun verifyTests(test: FeedLoadTest) {
        coVerify {
            anyConstructed<SomeRepository>().someMethod(test.isRealtime, any())
        }
        confirmVerified(repository)
    }
}

Тесты не пройдены

Для каждого запускаемого параметризованного теста количество подтвержденных и записанных вызовов увеличивается кумулятивно.

java .lang.AssertionError: Ошибка подтверждения подтверждения

Количество подтвержденных вызовов: 368 Количество зарегистрированных вызовов: 57

Не подтвержденные вызовы:

Трассировки стека:

в io.mockk.impl.recording.CommonVerificationAcknowledger.acknowledgeVerified (CommonVerificationAcknowledger.kt: 36) в io.mockk.MockKDsl.internalConfirmVerified (API.kt: 272k в io. MockKKt.confirmVerified (MockK.kt: 314) в app.coinverse.contentviewmodel.tests.FeedLoadTests.verifyTests (FeedLoadTests.kt: 201) в app.coinverse.contentviewmodel.tests.FeedLoadTests.TecLest.TestsToTestsTact в app.coinverse.contentviewmodel.tests.FeedLoadTests $ загрузка фида $ 1.invokeSuspend (FeedLoadTests.kt: 67) в app.coinverse.contentviewmodel.tests.FeedLoadTests $ загрузка фида $ 1.invoke (FeedLoadTests.kt) в kotlin .TestBuildersKt $ runBlockingTest $ deferred $ 1.invokeSuspend (TestBuilders.kt: 50) в kotlin .coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt: 33) в kotlinx.coroutines.DispatchedTask.run (Dispatched.kt: 241) в kotlinx.coroutines.test.TestCoroutineDispatcher.dispatch (TestCoroutineDispatcher.kt: 142) в kotlinx.coroutines.DispatchedKt.resume3ancel kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable (Cancellable.kt: 26) в kotlinx.coroutines.CoroutineStart.invoke (CoroutineStart.kt: 109) в kotlinx.coroutines.AbstractCoroutine.start. .BuildersKt__Builders_commonKt.asyn c (Builders.common.kt: 89) в kotlinx.coroutines.BuildersKt.asyn c (неизвестный источник) в kotlinx.coroutines.BuildersKt__Builders_commonKt.async $ default: Builders.com: по умолчанию: Builders.com в kotlinx.coroutines.BuildersKt.async $ default (неизвестный источник) в kotlinx.coroutines.test.TestBuildersKt.runBlockingTest (TestBuilders.kt: 49) в kotlinx.coroutines.test.TestBuildersKt.runBlockingTest 78: at) (Test) .coinverse.contentviewmodel.tests.FeedLoadTests.Feed Load (FeedLoadTests.kt: 56) в sun.reflect.NativeMethodAccessorImpl. invoke0 (родной метод) в sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl. java: 62) в sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl. * 1148.homb. invoke (Method. java: 498) на org.junit.platform.commons.util.ReflectionUtils.invokeMethod (ReflectionUtils. java: 675) на org.junit.jupiter.engine.execution.MethodInvocation.proceed (MethodInvocation. java: 60) в org.junit.jupiter.engine.execution.InvocationInterceptorChain $ ValidatingInvocation.proceed (InvocationInterceptorChain. java: 125) в org.junit.jupiter.engine.extension.TimeoutExtension.intercept *: 132) в org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod (TimeoutExtension. java: 124) в org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestTemplateMethod (TimeoutExtension. java): 81 в org.junit.jupiter.engine.execution.ExecutableInvoker $ ReflectiveInterceptorCall.lambda $ ofVoidMethod $ 0 (ExecutableInvo ker. java: 115) в org.junit.jupiter.engine.execution.ExecutableInvoker.lambda $ invoke $ 0 (ExecutableInvoker. java: 105) в org.junit.jupiter.engine.execution.InvocationInterceptorChain $ InterceceInedocpropro (InvocationInterceptorChain. java: 104) по адресу org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed (InvocationInterceptorChain. java: 62) по адресу org.junit.jupiter.engine.execution.Inorin 1161 *: 43) в org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke (InvocationInterceptorChain. java: 35) в org.junit.jupiter.engine.execution.ExecutableInvoker.invoke (ExecutableInvoker * 1163). . в org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute (ThrowableCollector. java: 73) в org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod (TestMethodTestDescriptor. java: 198) в org.junit.jupiter.engine.descriptor. ) в org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute (TestMethodTestDescriptor. java: 69) в org.junit.platform.engine.support.hierarchical.NodeTestTestk.txt * * 5 *. 135) в org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute (ThrowableCollector. java: 73) в org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda $ executeRecksiveT $ 7 (выполнять. 1172 *: 125) в org.junit.platform.engine.support.hierarchical.Node.around (Node. java: 135) в org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda $ executeRecursively $ 8 ( NodeTestTask. java: 123) в org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute (ThrowableCollector. java: 73) в org.junit.p latform.engine.support.hierarchical.NodeTestTask.executeRecursively (NodeTestTask. java: 122) в org.junit.platform.engine.support.hierarchical.NodeTestTask.execute (NodeTestTask. * 11g. 80): Platinum junit.jupiter.engine.descriptor.TestTemplateTestDescriptor.execute (TestTemplateTestDescriptor. java: 138) в org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor.lambda $ execute * 2 * (TestTemplateTestDest) 1181 * at1 .util.stream.ForEachOps $ ForEachOp $ OfRef.accept (ForEachOps java:. 184) в java .util.stream.ReferencePipeline $ 3 $ 1.accept (ReferencePipeline. java: 193) в java .util.stream.ReferencePipeline $ 2 $ 1.accept (ReferencePipeline. java: 175) в java .util.stream.ReferencePipeline $ 3 $ 1.accept (ReferencePipeline. java: 193) в java .util.stream.ForEachOps $ ForEachOp $ OfRef.accept (ForEachOps. java: 184) в java .util .stream.ReferencePipeline $ 11 $ 1.accept (ReferencePipeline. java: 373) в java .util.stream.ReferencePipeline $ 3 $ 1.accept (ReferencePipeline. java: 193) в java .util.stream.ReferencePipeline $ 3 $ 1.accept (ReferencePipeline. java: 193) в java .util.stream.ReferencePipeline $ 3 $ 1.accept (ReferencePipeline. java: 193) в java .util.stream.ForEachOps $ ForEachOp $ OfRef .accept (ForEachOps. java: 184) в java .util.stream.ReferencePipeline $ 3 $ 1.accept (ReferencePipeline. java: 193) в java .util.Spliterators $ ArraySpliterator.forEachRemaining (Spliterators. java: 948) в java .util.stream.ReferencePipeline $ Head.forEach (ReferencePipeline. java: 580) в java .util.stream.R eferencePipeline $ 7 $ 1.accept (ReferencePipeline. java: 270) в java .util.stream.ReferencePipeline $ 3 $ 1.accept (ReferencePipeline. java: 193) в java .util.stream.ReferencePipeline $ 3 $ 1. принять (ReferencePipeline. java: 193) в java .util.Spliterators $ ArraySpliterator.forEachRemaining (Spliterators. java: 948) в java .util.stream.AbstractPipeline.copyInto (AbstractPipeline. java: 481) в java .util.stream.AbstractPipeline.wrapAndCopyInto (AbstractPipeline. java: 471) в java .util.stream.ForEachOps $ ForEachOp.evaluateSequential (ForEachOps. java: 151) в java .util.stream.ForEachOps $ ForEachOp $ OfRef.evaluateSequential (ForEachOps. java: 174) в java .util.stream.AbstractPipeline.evaluate (AbstractPipeline. java: 234) в java .util.stream .ReferencePipeline.forEach (ReferencePipeline. java: 418) в java .util.stream.ReferencePipeline $ 7 $ 1.accept (ReferencePipeline. java: 270) в java .util.stream.ReferencePipeline $ 3 $ 1.accept (ReferencePipeline. java: 193) в java .util.stream.ReferencePipeline $ 3 $ 1.accept (ReferencePipeline. java: 193) в java .util.stream.ReferencePipeline $ 3 $ 1.accept (ReferencePipeline. java: 193) в java .util.ArrayList $ ArrayListSpliterator.forEachRemaining (ArrayList . java: 1382) в java .util.stream.AbstractPipeline.copyInto (AbstractPipeline. java: 481) в java .util.stream.AbstractPipeline.wrapAndCopyInto (AbstractPipeline. java: 471) в java .util.stream.ForEachOps $ ForEachOp.evaluateSequential (ForEachOps. java: 151) в java .util.stream.ForEachOps $ ForEachOp $ OfRef.evaluateSequential (ForEachOps. java: 174) в 12 * * .util.stream.AbstractPipeline.evaluate (AbstractPipeline. java: 234) в java .util.stream.ReferencePipeline.forEach (ReferencePipeline. java: 418) в java .util.stream.ReferencePipeline $ 7 $ 1.accept (ReferencePipeline. java: 270) в java .util.ArrayList $ ArrayListSpliterator.forEachRemaining (ArrayList. java: 1382) в java .util.stream.AbstractPipeline.copyInto (AbstractPipeline. * 1255) *: 481) в java .util.stream.AbstractPipeline.wrapAndCopyInto ( AbstractPipeline. java: 471) в java .util.stream.ForEachOps $ ForEachOp.evaluateSequential (ForEachOps. java: 151) в java .util.stream.ForEachOps $ ForEachOp $ OfRef.evaluateSequential (ForEOPSO). java: 174) в java .util.stream.AbstractPipeline.evaluate (AbstractPipeline. java: 234) в java .util.stream.ReferencePipeline.forEach (ReferencePipeline. java: 418) в орг. .junit.jupiter.engine.descriptor. .engine.support.hierarchical.NodeTestTask.lambda $ executeRecursively $ 5 (NodeTestTask. java: 135) в org.junit.platform.engine. support.hierarchical.ThrowableCollector.execute (ThrowableCollector. java: 73) в org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda $ executeRecursively $ 7 (NodeTestTask. java: 125). .engine.support.hierarchical.Node.around (Node. java: 135) в org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda $ executeRecursively $ 8 (NodeTestTask. java: 123) в орг. junit.platform.engine.support.hierarchical.ThrowableCollector.execute (ThrowableCollector. java: 73) в org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively (NodeTestTasg: 122): 122). junit.platform.engine.support.hierarchical.NodeTestTask.execute (NodeTestTask. java: 80) в java .util.ArrayList.forEach (ArrayList. java: 1257) в org.junit.platform.engine. support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll (SameThreadHierarchicalTestExecutorService. java: 38) в org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda $ executeRecursively $ (выполнить $ 5) ( NodeTestTask. java: 139) в org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute (ThrowableCollector. java: 73) в org.junit.platform.engine.support.hierarchical.NodeTestTas $ executeRecursively $ 7 (NodeTestTask. java: 125) в org.junit.platform.engine.support.hierarchical.Node.around (Node. java: 135) в org.junit.platform.engine.support.hierarchical.NodeTestTask .lambda $ executeRecursively $ 8 (NodeTestTask. java: 123) в org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute (ThrowableCollector. java: 73) в org.junit.platform.engine.su ierarchical.NodeTestTask.executeRecursively (NodeTestTask. java: 122) в org.junit.platform.engine.support.hierarchical.NodeTestTask.execute (NodeTestTask. java: 80) в java list for. ArrayList. java: 1257) в org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll (SameThreadHierarchicalTestExecutorService. java: 38) в org.junit.psupport.gine. ierarchical.NodeTestTask.lambda $ executeRecursively $ 5 (NodeTestTask. java: 139) в org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute (ThrowableCollector. java: 73) в org. .support.hierarchical.NodeTestTask.lambda $ executeRecursively $ 7 (NodeTestTask. java: 125) в org.junit.platform.engine.support.hierarchical.Node.around (Узел. java: 135) в org.junit. platform.engine.support.hierarchical.NodeTestTask.lambda $ executeRecursively $ 8 (NodeTestTask. java: 123) в org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute (ThrowableCollector: 73 или 1295) * .junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively (NodeTestTask. java: 122) в org.junit.platform.engine.support.hierarchical.NodeTestTask.execute (NodeTestTasg) 80 (NodeTestTasg): 80 .junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit (SameThreadHierarchicalTestExecutorService. java: 32) в org.junit.platform.engine.support.hiera rchical.HierarchicalTestExecutor.execute (HierarchicalTestExecutor. java: 57) в org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute (HierarchicalTestEngine. java: 51) в orgc.jt. DefaultLauncher.execute (DefaultLauncher. java: 229) в org.junit.platform.launcher.core.DefaultLauncher.lambda $ execute $ 6 (DefaultLauncher. java: 197) в org.junit.platform.launcher.core.DefaultLauncher .withInterceptedStreams (DefaultLauncher. java: 211) в org.junit.platform.launcher.core.DefaultLauncher.execute (DefaultLauncher. java: 191) в org.junit.platform.launcher.core.DefaultLauncher.execute ( . java: 128) в com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs (JUnit5IdeaTestRunner. java: 69) в com.intellij.rt.execution. junit.IdeaTestRunner $ Repeater.startRunnerWithArgs (IdeaTestRunner. java: 47) в com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart (JUnitStarter. java: 242). JUnitStarter.main (JUnitStarter. java: 70)

Попытки решения

1. Переместите mockkConstructor с beforeAll на beforeEach Метод жизненного цикла теста JUnit 5.

Это кажется улучшением, поскольку проверенные и записанные тесты не накапливают каждый параметризованный тестовый цикл, как раньше. Скорее, правильная сигнатура метода не найдена.

Тесты не пройдены

java .lang.AssertionError: Ошибка подтверждения подтверждения

Количество подтвержденных вызовов: 0 Количество записанных вызовов: 2

Не подтвержденные вызовы: 1) SomeRepository (mockkConstructor ()). GetMainFeedNetwork (false, Timestamp (seconds = 1581980238, наносекунд = 352000000)) 2) SomeRepository (mockkConstructor ()). GetMainFeedRoom (getMainFeedRoom (getMainFeedRoom (getMainFeedRoom ()). Отметка времени (секунды = 1581980238, наносекунды = 352000000))

2. Удалите переменную экземпляра уровня класса теста для репозитория и передайте репозиторий через параметры метода.

Хранилище создается как новое значение внутри каждого параметризованного теста, а затем передается в качестве параметра mockComponents и verifyTests.

Ошибки трассировки стека не указывают на отсутствие каких-либо методов. Не подтвержденные звонки пусто.

Тесты не пройдены

java .lang.AssertionError: Ошибка подтверждения подтверждения

Количество подтвержденных вызовов: 2 Количество зарегистрированных вызовов: 2

Не подтвержденные звонки:

3. Очистить / разблокировать репозиторий в beforeEach с использованием clearConstructorMockk и unmockkConstructor.

clearConstructorMockk и unmockkConstructor были протестированы по отдельности и получили те же сообщения об ошибках, что и Attempted Solution # 2.

1 Ответ

0 голосов
/ 19 февраля 2020

Смоделируйте класс репозитория с помощью mockkClass() и сохраните в качестве значения экземпляра

@ExtendWith(SomeTestExtension::class)
class SomeParamUnitTest(val testDispatcher: TestCoroutineDispatcher) {

    private fun TestLoad() = teedLoadTestCases()
    private val repository = mockkClass(SomeRepository::class)
    private lateinit var someViewModel: SomeViewModel

    @AfterAll
    fun afterAll() {
        unmockkAll()
    }

    @ParameterizedTest
    @MethodSource("TestLoad")
    fun `Feed Load`(test: FeedLoadTest) = testDispatcher.runBlockingTest {
        mockComponents(test)
        someViewModel = SomeViewModel(...)
        assertContentList(...) // Working as expected.
        verifyTests(test)
    }

    private fun mockComponents(test: FeedLoadTest) {
        ...
        coEvery {
            repository.someMethod(test.isRealtime, any())
        } returns mockSomeMethod(test.mockFeedList, test.status)
        ...
    }

    private fun verifyTests(test: FeedLoadTest) {
        coVerify {
            repository.someMethod(test.isRealtime, any())
        }
        confirmVerified(repository)
    }
}
...