EDIT: For future readers. I don't know if this question will help you very much.The logic of the fun has changed drastically so I am closing the question, but won't delete it.

I am trying to write some unit tests for my ViewModel. I am using Mockk and Junit5.

What should happen: The mockked repository returns fakeresponse, I call the fun in the VM, it sets the livedata to be the fake response data.

What actually happens:

Exception in thread "DefaultDispatcher-worker-2 @coroutine#1" io.mockk.MockKException: no answer found for: DrillRepository(#1).loadDrillTypes() at io.mockk.impl.stub.MockKStub.defaultAnswer(MockKStub.kt:90) at io.mockk.impl.stub.MockKStub.answer(MockKStub.kt:42) at io.mockk.impl.recording.states.AnsweringState.call(AnsweringState.kt:16) at io.mockk.impl.recording.CommonCallRecorder.call(CommonCallRecorder.kt:53) at io.mockk.impl.stub.MockKStub.handleInvocation(MockKStub.kt:263) at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler$1.invocation(JvmMockFactoryHelper.kt:25) at io.mockk.proxy.jvm.advice.Interceptor.call(Interceptor.kt:20) at com.nikolam.basketpro.data.DrillRepository.loadDrillTypes(DrillRepository.kt:11) at com.nikolam.basketpro.ui.drills.selection.DrillsSelectionViewModel$fetchDrillTypes$1.invokeSuspend(DrillsSelectionViewModel.kt:41) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594) at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:740) org.opentest4j.AssertionFailedError: Expected :[DrillsType(drillType_title=title1, drillType_imageUrl=url1), DrillsType(drillType_title=title2, drillType_imageUrl=url2)] Actual :null <Click to see difference> at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55) at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62) at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182) at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177) at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1124) at com.nikolam.basketpro.ui.drills.selection.DrillsSelectionViewModelTest.drillTypeListWillBePopulatedOnSuccess(DrillsSelectionViewModelTest.kt:43) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84) at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115) at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:205) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:201) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at java.util.ArrayList.forEach(ArrayList.java:1257) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at java.util.ArrayList.forEach(ArrayList.java:1257) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248) at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211) at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

This is the test

@ExtendWith(value = [InstantExecutorExtension::class, TestSchedulerExtension::class]) internal class DrillsSelectionViewModelTest{ val mockkDrillRepository = mockk<DrillRepository>() lateinit var drillsSelectionViewModel : DrillsSelectionViewModel @BeforeEach fun setup(){ drillsSelectionViewModel = DrillsSelectionViewModel(mockkDrillRepository) val fakeResponse = Observable.just(DrillsType("title1", "url1"), DrillsType("title2","url2")) every{ mockkDrillRepository.loadDrillTypes()} returns fakeResponse } @Test fun `should update the livedata with correct data from the repository`(){ drillsSelectionViewModel.fetchDrillTypes() assertEquals(arrayListOf(DrillsType("title1", "url1"), DrillsType("title2","url2")), getValue(drillsSelectionViewModel.drillsTypeList.value)) } }

These are the two classes that are extended in the test

class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback { override fun beforeEach(context: ExtensionContext?) { ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() { override fun executeOnDiskIO(runnable: Runnable) { runnable.run() } override fun postToMainThread(runnable: Runnable) { runnable.run() } override fun isMainThread(): Boolean { return true } }) } override fun afterEach(context: ExtensionContext?) { ArchTaskExecutor.getInstance().setDelegate(null) } }

class TestSchedulerExtension : BeforeTestExecutionCallback, AfterTestExecutionCallback { override fun beforeTestExecution(context: ExtensionContext?) { RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() } RxJavaPlugins.setComputationSchedulerHandler { Schedulers.trampoline() } RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() } RxAndroidPlugins.setMainThreadSchedulerHandler { Schedulers.trampoline() } } override fun afterTestExecution(context: ExtensionContext?) { RxJavaPlugins.reset() RxAndroidPlugins.reset() } }

This is the SUT

class DrillsSelectionViewModel(private val repository: DrillRepository): ViewModel() { private val compositeDisposable = CompositeDisposable() private var _drillsTypeList = MutableLiveData<ArrayList<DrillsType>>() val drillsTypeList: LiveData<ArrayList<DrillsType>> get() = _drillsTypeList init{ fetchDrillTypes() } fun fetchDrillTypes() { val list = ArrayList<DrillsType>() viewModelScope.launch(Dispatchers.IO) { compositeDisposable += repository.loadDrillTypes() .subscribeWith(object : DisposableObserver<DrillsType>() { override fun onError(e: Throwable) { Log.d("TAG", e?.message) } override fun onNext(data: DrillsType) { Log.d("TAG", "data is " + data.toString()) list.add(data) } override fun onComplete() { Log.d("TAG", "COMPLETE") _drillsTypeList.postValue(list) } }) } } override fun onCleared() { super.onCleared() if (!compositeDisposable.isDisposed) { compositeDisposable.dispose() } } }

The DOC

fun loadDrillTypes(): Observable<DrillsType> { return remoteDataSource.loadDrillTypes() } fun loadDrillList(drillType : String): Observable<Drill> { return remoteDataSource.loadDrillList(drillType) } }

What I tried:

Removing the coroutines from the mix. I thought that removing "viewmodelscope.launch" would make the test work as I thought it had something to do with coroutines and them not finishing or something in time or not listening on the proper thread. Tried using the 2 extended classes posted below(to replace the Instant Execution Rule in JUnit4) Googling a lot to find similar issues...

Sorry for the long post, any more info or code that you need feel free to ask.If possible also point out some mistakes/ things that could be done better. Any answer with explanation is much appreciated. I really want to understand what is the underlying issue. Thank you very much!

edit: I am considering it might be something due to RXKotlin