stephanenicolas / toothpick

A scope tree based Dependency Injection (DI) library for Java / Kotlin / Android.
Apache License 2.0
1.12k stars 115 forks source link

Tests with TP - Mockk #333

Open Claire1817 opened 5 years ago

Claire1817 commented 5 years ago

Hello !

I didn't find a solution in the others issues or in the samples.
I think this case can help the others developpers. I'm trying to do some simple tests on my useCase and i use Mockk and jUnit.

I have an error in the injection of my object geocoder, this is the stacktrace of my error:

"C:\Program Files\Android\Android Studio\jre\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\Android\Android Studio\lib\idea_rt.jar=54730:C:\Program Files\Android\Android Studio\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Android\Android Studio\lib\idea_rt.jar;C:\Program Files\Android\Android Studio\plugins\junit\lib\junit-rt.jar;C:\Program Files\Android\Android Studio\plugins\junit\lib\junit5-rt.jar;C:\Users\cgizard\AppData\Local\Android\Sdk\platforms\android-28\data\res;C:\Users\cgizard\Documents\android\TP\tp-hands-on\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes;C:\Users\cgizard\Documents\android\TP\tp-hands-on\app\build\tmp\kotlin-classes\debugUnitTest;C:\Users\cgizard\Documents\android\TP\tp-hands-on\app\build\tmp\kapt3\classes\debugUnitTest;C:\Users\cgizard\Documents\android\TP\tp-hands-on\app\build\tmp\kotlin-classes\debug;C:\Users\cgizard\Documents\android\TP\tp-hands-on\app\build\tmp\kapt3\classes\debug;C:\Users\cgizard\Documents\android\TP\tp-hands-on\app\build\generated\res\resValues\debug;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\c6795502c19e8a5d6eabaa9cdbd66997\res;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\c6795502c19e8a5d6eabaa9cdbd66997\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\io.mockk\mockk-common\1.9.3\7f2d6bc3390a76edeba34947e0de6d2314645f25\mockk-common-1.9.3.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib-common\1.3.21\f30e4a9897913e53d778f564110bafa1fef46643\kotlin-stdlib-common-1.3.21.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\8ca8272201e593e52e9c3f9fb373ec63\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\d70717a9c47ffb8f42325f3fa33c6ca2\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\865f91dd4d720ab709673f330b189b38\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\ed34f65b24a5f2d904fafe9658e3a656\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\ed34f65b24a5f2d904fafe9658e3a656\res;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\b109d70fffa07d2a87e27e2745d9a48f\res;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\b109d70fffa07d2a87e27e2745d9a48f\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\org.objenesis\objenesis\3.0.1\11cfac598df9dc48bb9ed9357ed04212694b7808\objenesis-3.0.1.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.github.stephanenicolas.toothpick\toothpick\2.1.0\b64a81c97ae4114edf642cf6682a48e5f9ea4f60\toothpick-2.1.0.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\b59126231dffdecbf434e4e92ad501cf\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\18d32e120f53d5d4b2d0a211db428579\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\e651c45e32b929d002b4f3d9eba6cac9\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\77409a87a50559dc4ac8ea7c93176285\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\8794ace6b2e185e1e5931bee308a883d\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\1ff7fdbf7685aa8ab89f930f5f504053\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\io.mockk\mockk\1.9.3\d26e5a4a7fd4ecb5635b28a0b70960ab0b20556d\mockk-1.9.3.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\d49a08e6720db72b176b6b86a81b8c8b\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\io.mockk\mockk-dsl-jvm\1.9.3\63de994ae1f30b06395325258d2e5683a8bdd525\mockk-dsl-jvm-1.9.3.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\3b8b7528a9c480055b0bc8a6c4ce43c3\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\f297cf209a39d8e848f12e919706ecd2\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\8f939a3fffca1e316e412afe4fe4d171\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\019635dc309cb5332d6b155403b9742a\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\55106300991dd47fd137f6020ad54dbf\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\88e4678c43eceaa0a35b933e12030f52\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.github.stephanenicolas.toothpick\toothpick-testing\2.1.0\6f914c89fad3a21a462ddbabcd6b095413a9eef\toothpick-testing-2.1.0.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\javax.inject\javax.inject\1\6975da39a7040257bd51d21a231b76c915872d38\javax.inject-1.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\0c89adce95726f4a2de6ecda09361028\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\5ae993c5672e29747357921022065e94\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.squareup.retrofit2\retrofit\2.3.0\bcacde6a8ccedcc56c127403d26b76072fe6214d\retrofit-2.3.0.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\6a0900a69f16da6bc985ac53699576d4\res;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\6a0900a69f16da6bc985ac53699576d4\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\ce28f4e7a09920aeb4fdaf1e1a13dae6\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.squareup.okhttp3\okhttp\3.8.0\5a11f020cce2d11eb71ba916700600e18c4547e7\okhttp-3.8.0.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\aa8af142f22b4c172172be6c6e546968\res;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\aa8af142f22b4c172172be6c6e546968\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\b92615a755bc2258b620e1f8d5713a4c\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\34a3c90db54e28834292217e153b69ab\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib\1.3.21\4bcc2012b84840e19e1e28074284cac908be0295\kotlin-stdlib-1.3.21.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\78254233f2a4c90a6ee5177e9321dc5b\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib-jdk7\1.3.21\d207ce2c9bcf17dc8e51bab4dbfdac4d013e7138\kotlin-stdlib-jdk7-1.3.21.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\2a071358abfb82c91f1ff48fe35181b2\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\7e80f8dacd5ecd74c1e36fb960219d2c\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\c64e06ab3d5d05b81846497ed544c269\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\45b75ecf8e521d64018454f6e9db0f0d\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\c60f15e29098cfd94b790bbccd36bba9\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\c60f15e29098cfd94b790bbccd36bba9\res;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.squareup.okio\okio\1.13.0\a9283170b7305c8d92d25aff02a6ab7e45d06cbe\okio-1.13.0.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\3a0a92403d15aea2b2aefd4f4d8ae956\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\3a0a92403d15aea2b2aefd4f4d8ae956\res;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\org.reactivestreams\reactive-streams\1.0.2\323964c36556eb0e6209f65c1cef72b53b461ab8\reactive-streams-1.0.2.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\io.reactivex.rxjava2\rxjava\2.2.7\394ee6d9e141acb9fbf9daf228fd8cd789eaa49a\rxjava-2.2.7.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\ad05011a2d5ce5783c8e5daed5d668e2\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-reflect\1.3.0\6fd129fd9ba8581f2cb9c58bfd431dda4ee0457e\kotlin-reflect-1.3.0.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.squareup.retrofit2\converter-moshi\2.3.0\6ad87c2038269d2574fcd90765ce15c74d6bb993\converter-moshi-2.3.0.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\f0dd84b927ccee47e41fc467d6ae3d80\res;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\f0dd84b927ccee47e41fc467d6ae3d80\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\7c7fdc87993bbead6e1a943997fb89d2\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\androidx.annotation\annotation\1.0.1\2dfd8f6b2a8fc466a1ae4e329fb79cd580f6393f\annotation-1.0.1.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\io.mockk\mockk-dsl\1.9.3\4f158b4ed161bc4373356733088e09491ecd13da\mockk-dsl-1.9.3.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\io.mockk\mockk-agent-jvm\1.9.3\d6f7b4d893caf1cda2bb28ee637b247ced5d5eab\mockk-agent-jvm-1.9.3.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\androidx.lifecycle\lifecycle-common\2.0.0\e070ffae07452331bc5684734fce6831d531785c\lifecycle-common-2.0.0.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\12ff022c1706ebb82f05509eb6f947ac\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\4c4e131b467b4bb3c6fa9b7ccfa245e5\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\ad6da781350574fb8d2326ced99875d5\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\androidx.constraintlayout\constraintlayout-solver\1.1.3\54abe9ffb22cc9019b0b6fcc10f185cc4e67b34e\constraintlayout-solver-1.1.3.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\io.mockk\mockk-agent-common\1.9.3\50f32c350cb9018a62e6de2bed710acf675f887b\mockk-agent-common-1.9.3.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\org.jetbrains\annotations\16.0.1\c1a6655cebcac68e63e4c24d23f573035032eb2a\annotations-16.0.1.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\c829d1c7b59b8404cddb78216b671cec\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\202469d5ef282e44cdaae0fb89545d95\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.github.stephanenicolas.toothpick\toothpick-runtime\2.1.0\e4456493ef995a4e7c4a541a64106a5b4c78a685\toothpick-runtime-2.1.0.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\androidx.arch.core\core-common\2.0.0\bb21b9a11761451b51624ac428d1f1bb5deeac38\core-common-2.0.0.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\ee64b893024ba1ab76bfb22946847ae2\res;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\ee64b893024ba1ab76bfb22946847ae2\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\933458fe4cba7ca8ce1be45a5976cafc\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\933458fe4cba7ca8ce1be45a5976cafc\res;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\junit\junit\4.12\2973d150c0dc1fefe998f834810d68f278ea58ec\junit-4.12.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\org.hamcrest\hamcrest-core\1.3\42a25dc3219429f0e5d060061f71acb49bf010a0\hamcrest-core-1.3.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\922a0c9297f2d03379e4b195504ad7ef\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\922a0c9297f2d03379e4b195504ad7ef\res;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.github.stephanenicolas.toothpick\toothpick-testing-junit4\2.1.0\1a0a91062eec70ce89df58445ce3ed464710f6b4\toothpick-testing-junit4-2.1.0.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.squareup.picasso\picasso\2.5.2\7446d06ec8d4f7ffcc53f1da37c95f200dcb9387\picasso-2.5.2.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\de0270a3cec0e24071fbe4e8f6f029fe\jars\classes.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.squareup.moshi\moshi\1.4.0\d67ad038adfd55dc276488a54f4944dfae5ff8ec\moshi-1.4.0.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\com.squareup.retrofit2\adapter-rxjava2\2.3.0\f436637f9500ab5b8bc32afe556373180894b4a5\adapter-rxjava2-2.3.0.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\d4e2cc3dca16e6dad544f6ba21d3d8ba\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\d4e2cc3dca16e6dad544f6ba21d3d8ba\res;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\io.mockk\mockk-agent-api\1.9.3\467c1fd4867fc49b98a7de421a717b619767abfe\mockk-agent-api-1.9.3.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\net.bytebuddy\byte-buddy-agent\1.9.10\9674aba5ee793e54b864952b001166848da0f26b\byte-buddy-agent-1.9.10.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\net.bytebuddy\byte-buddy\1.9.10\211a2b4d3df1eeef2a6cacf78d74a1f725e7a840\byte-buddy-1.9.10.jar;C:\Users\cgizard\.gradle\caches\modules-2\files-2.1\androidx.collection\collection\1.0.0\42858b26cafdaa69b6149f45dfc2894007bc2c7a\collection-1.0.0.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\86a27f23c9e1b592a24a90599f793079\jars\classes.jar;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\d3aa18d38fcbd738ef38c227aa305119\jars\classes.jar;C:\Users\cgizard\Documents\android\TP\tp-hands-on\app\build\intermediates\sourceFolderJavaResources\test\debug;C:\Users\cgizard\Documents\android\TP\tp-hands-on\app\build\intermediates\sourceFolderJavaResources\debug;C:\Users\cgizard\.gradle\caches\transforms-2\files-2.1\ccfdb9ac49af9cfd99a730012c5e5686\android.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 weather.ekamp.com.weatherappkotlin.UseCase.GetAddressUseCaseTest

toothpick.locators.NoFactoryFoundException: No factory could be found for class android.location.Geocoder. Check that the class has either a @Inject annotated constructor or contains @Inject annotated members.

    at toothpick.locators.FactoryLocator.getFactory(FactoryLocator.java:21)
    at toothpick.ScopeImpl.lookupProvider(ScopeImpl.java:329)
    at toothpick.ScopeImpl.getInstance(ScopeImpl.java:58)
    at toothpick.ScopeImpl.getInstance(ScopeImpl.java:49)
    at weather.ekamp.com.weatherappkotlin.usecase.GetAddressUseCase__MemberInjector.inject(GetAddressUseCase__MemberInjector.java:12)
    at weather.ekamp.com.weatherappkotlin.usecase.GetAddressUseCase__MemberInjector.inject(GetAddressUseCase__MemberInjector.java:9)
    at toothpick.InjectorImpl.inject(InjectorImpl.java:23)
    at toothpick.Toothpick.inject(Toothpick.java:163)
    at weather.ekamp.com.weatherappkotlin.usecase.GetAddressUseCase.<init>(GetAddressUseCase.kt:21)
    at weather.ekamp.com.weatherappkotlin.UseCase.GetAddressUseCaseTest.getAddressUseCaseTest(GetAddressUseCaseTest.kt:60)
    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.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    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)
Caused by: java.lang.ClassNotFoundException: android.location.Geocoder__Factory
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at toothpick.locators.FactoryLocator.getFactory(FactoryLocator.java:18)
    ... 32 more

Process finished with exit code -1

My useCase:

package weather.ekamp.com.weatherappkotlin.usecase

import android.app.Application
import android.location.Location
import io.reactivex.Observable
import toothpick.Toothpick
import weather.ekamp.com.weatherappkotlin.model.location.UserLocationManager
import javax.inject.Inject

class GetLocationUseCase constructor(application: Application) {

    @Inject
    lateinit var userLocationManager: UserLocationManager

    init {
        val activityScope = Toothpick.openScopes(application, this)
        Toothpick.inject(this, activityScope)
    }

    fun execute(): Observable<Location> {
        return userLocationManager.observeLocation()
    }
}

and my test file (my test does nothing actually, i will improve my test when my problem will be solve).

package weather.ekamp.com.weatherappkotlin.UseCase

import android.app.Application
import android.location.Address
import android.location.Geocoder
import android.location.Location
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.reactivex.android.plugins.RxAndroidPlugins
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.BehaviorSubject
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import toothpick.testing.ToothPickRule
import weather.ekamp.com.weatherappkotlin.model.location.UserLocationManager
import weather.ekamp.com.weatherappkotlin.usecase.GetAddressUseCase
import java.util.Locale

class GetAddressUseCaseTest {

    private val toothPickRule = ToothPickRule(this)
        @Rule get

    @MockK
    internal lateinit var application: Application

    @MockK
    internal lateinit var geocoder: Geocoder

    @MockK
    internal lateinit var userLocationManager: UserLocationManager

    @Before
    fun setup() {
        MockKAnnotations.init(this)
        toothPickRule.setScopeName(application)
        toothPickRule.inject(this)
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
    }

    @Test
    fun getAddressUseCaseTest() {
        // Given
        val test: BehaviorSubject<Location> = BehaviorSubject.create()
        val latitude = 65.2
        val longitude = 22.8
        val address = Address(Locale("France"))
        val listOfAddress = ArrayList<Address>().apply {
            add(address)
        }
        every { geocoder.getFromLocation(latitude, longitude, 1) } returns listOfAddress
        every { userLocationManager.observeLocation() } returns test

        // When
        val expectedResult = GetAddressUseCase(application).execute()

        // Then

    }
}

I also have a binding file which looks like that:

package weather.ekamp.com.weatherappkotlin.common.application

import android.app.Application
import android.location.Geocoder
import okhttp3.OkHttpClient
import retrofit2.CallAdapter
import retrofit2.Converter
import toothpick.config.Module
import weather.ekamp.com.weatherappkotlin.common.inject.GeocoderProvider
import weather.ekamp.com.weatherappkotlin.common.inject.network.WeatherApiProvider
import weather.ekamp.com.weatherappkotlin.common.inject.network.generic.CallAdapterFactoryProvider
import weather.ekamp.com.weatherappkotlin.common.inject.network.generic.ConvertorFactoryProvider
import weather.ekamp.com.weatherappkotlin.common.inject.network.generic.OkHttpClientProvider
import weather.ekamp.com.weatherappkotlin.model.weather.WeatherApi

class WeatherApplicationModule(application: Application) : Module() {

    init {
        // Manager
        bind(Geocoder::class.java).toProviderInstance(GeocoderProvider(application))

        // web Api
        bind(WeatherApi::class.java).toProvider(WeatherApiProvider::class.java)

        // OkHttp
        bind(OkHttpClient::class.java).toProvider(OkHttpClientProvider::class.java).providesSingletonInScope()
        bind(Converter.Factory::class.java).toProvider(ConvertorFactoryProvider::class.java)
        bind(CallAdapter.Factory::class.java).toProvider(CallAdapterFactoryProvider::class.java)
    }
}

I install my modules in the application. My useCase is call in a viewModel which doesn't contain TP.

I also try to Inject my useCase directly in my test class but it's not working.

Does someone have an idea ?

Thank's !

dlemures commented 5 years ago

Hi @ClaireGizard ,

I see a few problems there:

  1. Inside the test class, i see that you are calling toothPickRule.inject(this), but I don't see any class being injected (the class under Test).
  2. I cannot see WeatherApplicationModule being installed inside the tests. Still, if you wanna use mocks for all your dependencies, you shouldn't need to install it.
  3. Right now ToothPickRule only grabs mocks from the annotations with name @Mock, supporting Mockito and EasyMock. Do you want to create an issue so we add support to MockK?

Anyway, to provide you a working code, I'd need to see the code that you are testing: GetAddressUseCase.

Claire1817 commented 5 years ago

Hi @dlemures ! Sorry for my late response. I tried to find a solution by creating another simple project. I solved the first and the third problem but i didn't find a solution for the second. In my second project i have an error on the test and i didn't find a solution and i hope you can help me. Here there is my code:

package com.example.viewmodeltests

import android.app.Application
import android.location.Address
import android.location.Geocoder
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Provider

class GetAddressUseCase @Inject constructor() {

    @Inject
    lateinit var geocoder: Geocoder

    private val latitude = 48.117266

    private val longitude = -1.6777926

    fun execute(): Single<Address> {
        return Single.just(geocoder.getFromLocation(latitude, longitude, 1))
                .filter { it.isNotEmpty() }
                .map { it.first() }
                .toSingle()
    }
}
class GeocoderProvider(var application: Application) : Provider<Geocoder> {

    ///////////////////////////////////////////////////////////////////////////
    // SPECIALISATION
    ///////////////////////////////////////////////////////////////////////////

    override fun get(): Geocoder {
        return Geocoder(application)
    }
}
package com.example.viewmodeltests

import io.reactivex.Single
import javax.inject.Inject

class GetTextUseCase @Inject constructor() {

    fun execute(): Single<String> {
        return Single.just("Plaf")
    }
}
package com.example.viewmodeltests;

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import kotlinx.android.synthetic.main.activity_main.*
import toothpick.Toothpick
import toothpick.config.Module
import javax.inject.Inject

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var viewModel: MainViewModelInterface

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
        Toothpick.openScopes(application, this).also {
            it.installModules(ActivityModule(this))
            Toothpick.inject(this, it)
        }

        viewModel.observeText()
                .observe(this, Observer {
                    it?.apply {
                        textView.text = it
                    }
                })

        viewModel.observeAddress()
                .observe(this, Observer {
                    it?.apply {
                        address.text = it
                    }
                })

        button.setOnClickListener {
            viewModel.updateText()
        }
    }
}
class ActivityModule(mainActivity: MainActivity) : Module() {
    init {
        bind(MainViewModelInterface::class.java).toInstance(ViewModelProviders.of(mainActivity).get(MainViewModel::class.java))
    }
}
package com.example.viewmodeltests

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import toothpick.Toothpick
import javax.inject.Inject

class MainViewModel @Inject constructor(application: Application) : MainViewModelInterface, AndroidViewModel(application) {

    @Inject
    lateinit var getTextUseCase: GetTextUseCase

    @Inject
    lateinit var getAddressUseCase: GetAddressUseCase

    private val text: MutableLiveData<String> = MutableLiveData()

    private val address: MutableLiveData<String> = MutableLiveData()

    private val disposableSubscriptions: CompositeDisposable = CompositeDisposable()

    init {
        Toothpick.openScopes(application, this).also {
            Toothpick.inject(this, it)
        }
        text.postValue("Plouf")
        address.postValue("Paris")
    }

    override fun observeText(): LiveData<String> {
        return text
    }

    override fun observeAddress(): LiveData<String> {
        return address
    }

    override fun onDestroy() {
        disposableSubscriptions.dispose()
        Toothpick.closeScope(this)
        super.onCleared()
    }

    override fun updateText() {

        getTextUseCase.execute()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSuccess {
                    text.postValue(it)
                }
                .subscribeWith(RxLogSingleSubscriber("getTextUseCase"))
                .also {
                    disposableSubscriptions.add(it)
                }

        getAddressUseCase.execute()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSuccess {
                    address.postValue(it.locality)
                }
                .subscribeWith(RxLogSingleSubscriber("getAddressUseCase"))
                .also {
                    disposableSubscriptions.add(it)
                }
    }
}
package com.example.viewmodeltests

import android.app.Application
import android.location.Geocoder
import toothpick.Toothpick
import toothpick.config.Module
import toothpick.configuration.Configuration
import toothpick.smoothie.module.SmoothieApplicationModule

class ViewModelTestsApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        initToothpick()
    }

    ///////////////////////////////////////////////////////////////////////////
    // PRIVATE METHODS
    ///////////////////////////////////////////////////////////////////////////

    private fun initToothpick() {
        val configuration = if (BuildConfig.DEBUG) Configuration.forDevelopment() else Configuration.forProduction()
        Toothpick.setConfiguration(configuration)

        Toothpick.openScope(this).let {
            it.installModules(
                    ApplicationModule(this),
                    SmoothieApplicationModule(this)
            )
        }
    }

    class ApplicationModule(application: Application) : Module() {
        init {
            bind(Geocoder::class.java).toProviderInstance(GeocoderProvider(application))
        }
    }

}

And my test file:

package com.example.viewmodeltests

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Observer
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.mockk
import io.mockk.verify
import io.reactivex.android.plugins.RxAndroidPlugins
import io.reactivex.schedulers.Schedulers
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import toothpick.testing.ToothPickRule

class MainViewModelTests {

    @get:Rule
    var toothPickRule = ToothPickRule(this, "test scope")

    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()

    @InjectMockKs
    lateinit var mainViewModel: MainViewModel

    @Before
    fun before() {
        MockKAnnotations.init(this)
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
        toothPickRule.inject(this)
    }

    @Test
    fun checkTextAfterInitialization() {
        // Given
        val observer = mockk<Observer<String>>()
        every { observer.onChanged(any()) } returns Unit

        // When
        mainViewModel.observeText().observeForever(observer)

        // Then
        verify {
            observer.onChanged("Plouf")
        }
    }

    // Test to check the state after a click
    @Test
    fun checkTextAfterClick() {
        // Given
        val observer = mockk<Observer<String>>()
        every { observer.onChanged(any()) } returns Unit

        // When
        mainViewModel.observeText().observeForever(observer)
        mainViewModel.updateText()

        // Then
        verify {
            observer.onChanged("Plaf")
        }
    }
}

And there is my stacktrace:

io.mockk.MockKException: No matching constructors found:
constructor(application : android.app.Application = <not able to lookup>)

    at io.mockk.impl.annotations.MockInjector.constructorInjection(MockInjector.kt:20)
    at io.mockk.impl.annotations.JvmMockInitializer.doInjection(JvmMockInitializer.kt:86)
    at io.mockk.impl.annotations.JvmMockInitializer.initMock(JvmMockInitializer.kt:64)
    at io.mockk.impl.annotations.JvmMockInitializer.initAnnotatedMocks(JvmMockInitializer.kt:22)
    at com.example.viewmodeltests.MainViewModelTests.before(MainViewModelTests.kt:88)
    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.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.mockito.internal.junit.JUnitRule$1.evaluateSafely(JUnitRule.java:52)
    at org.mockito.internal.junit.JUnitRule$1.evaluate(JUnitRule.java:43)
    at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
    at toothpick.testing.ToothPickStatement.evaluate(ToothPickStatement.java:17)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    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)

If succeded to do some tests on my use case but i failed on the viewModel. I don't understand why. Do you have an idea ?

Thank's

Claire

afaucogney commented 5 years ago

@Claire, I think your mockito rule is useless ;), even if it doesn't change the behavior/issue.

Le jeu. 1 août 2019 à 14:24, ClaireGizard notifications@github.com a écrit :

Hi @dlemures https://github.com/dlemures ! Sorry for my late response. I tried to find a solution by creating another simple project. I solved the first and the third problem but i didn't find a solution for the second. In my second project i have an error on the test and i didn't find a solution and i hope you can help me. Here there is my code:

package com.example.viewmodeltests

import android.app.Application import android.location.Address import android.location.Geocoder import io.reactivex.Single import javax.inject.Inject import javax.inject.Provider

class GetAddressUseCase @Inject constructor() {

@Inject
lateinit var geocoder: Geocoder

private val latitude = 48.117266

private val longitude = -1.6777926

fun execute(): Single<Address> {
    return Single.just(geocoder.getFromLocation(latitude, longitude, 1))
            .filter { it.isNotEmpty() }
            .map { it.first() }
            .toSingle()
}

}

class GeocoderProvider(var application: Application) : Provider {

///////////////////////////////////////////////////////////////////////////
// SPECIALISATION
///////////////////////////////////////////////////////////////////////////

override fun get(): Geocoder {
    return Geocoder(application)
}

}

package com.example.viewmodeltests

import io.reactivex.Single import javax.inject.Inject

class GetTextUseCase @Inject constructor() {

fun execute(): Single<String> {
    return Single.just("Plaf")
}

}

package com.example.viewmodeltests;

import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import kotlinx.android.synthetic.main.activity_main.* import toothpick.Toothpick import toothpick.config.Module import javax.inject.Inject

class MainActivity : AppCompatActivity() {

@Inject
lateinit var viewModel: MainViewModelInterface

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    //viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
    Toothpick.openScopes(application, this).also {
        it.installModules(ActivityModule(this))
        Toothpick.inject(this, it)
    }

    viewModel.observeText()
            .observe(this, Observer {
                it?.apply {
                    textView.text = it
                }
            })

    viewModel.observeAddress()
            .observe(this, Observer {
                it?.apply {
                    address.text = it
                }
            })

    button.setOnClickListener {
        viewModel.updateText()
    }
}

}

class ActivityModule(mainActivity: MainActivity) : Module() { init { bind(MainViewModelInterface::class.java).toInstance(ViewModelProviders.of(mainActivity).get(MainViewModel::class.java)) } }

package com.example.viewmodeltests

import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import toothpick.Toothpick import javax.inject.Inject

class MainViewModel @Inject constructor(application: Application) : MainViewModelInterface, AndroidViewModel(application) {

@Inject
lateinit var getTextUseCase: GetTextUseCase

@Inject
lateinit var getAddressUseCase: GetAddressUseCase

private val text: MutableLiveData<String> = MutableLiveData()

private val address: MutableLiveData<String> = MutableLiveData()

private val disposableSubscriptions: CompositeDisposable = CompositeDisposable()

init {
    Toothpick.openScopes(application, this).also {
        Toothpick.inject(this, it)
    }
    text.postValue("Plouf")
    address.postValue("Paris")
}

override fun observeText(): LiveData<String> {
    return text
}

override fun observeAddress(): LiveData<String> {
    return address
}

override fun onDestroy() {
    disposableSubscriptions.dispose()
    Toothpick.closeScope(this)
    super.onCleared()
}

override fun updateText() {

    getTextUseCase.execute()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnSuccess {
                text.postValue(it)
            }
            .subscribeWith(RxLogSingleSubscriber("getTextUseCase"))
            .also {
                disposableSubscriptions.add(it)
            }

    getAddressUseCase.execute()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnSuccess {
                address.postValue(it.locality)
            }
            .subscribeWith(RxLogSingleSubscriber("getAddressUseCase"))
            .also {
                disposableSubscriptions.add(it)
            }
}

}

package com.example.viewmodeltests

import android.app.Application import android.location.Geocoder import toothpick.Toothpick import toothpick.config.Module import toothpick.configuration.Configuration import toothpick.smoothie.module.SmoothieApplicationModule

class ViewModelTestsApplication : Application() {

override fun onCreate() {
    super.onCreate()
    initToothpick()
}

///////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
///////////////////////////////////////////////////////////////////////////

private fun initToothpick() {
    val configuration = if (BuildConfig.DEBUG) Configuration.forDevelopment() else Configuration.forProduction()
    Toothpick.setConfiguration(configuration)

    Toothpick.openScope(this).let {
        it.installModules(
                ApplicationModule(this),
                SmoothieApplicationModule(this)
        )
    }
}

class ApplicationModule(application: Application) : Module() {
    init {
        bind(Geocoder::class.java).toProviderInstance(GeocoderProvider(application))
    }
}

}

And my test file:

package com.example.viewmodeltests

import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.InjectMockKs import io.mockk.mockk import io.mockk.verify import io.reactivex.android.plugins.RxAndroidPlugins import io.reactivex.schedulers.Schedulers import org.junit.Before import org.junit.Rule import org.junit.Test import org.mockito.junit.MockitoJUnit import toothpick.testing.ToothPickRule

class MainViewModelTests {

@get:Rule
var mockitoRule = MockitoJUnit.rule()

@get:Rule
var toothPickRule = ToothPickRule(this, "test scope")

@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()

@InjectMockKs
lateinit var mainViewModel: MainViewModel

@Before
fun before() {
    MockKAnnotations.init(this)
    RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
    toothPickRule.inject(this)
}

@Test
fun checkTextAfterInitialization() {
    // Given
    val observer = mockk<Observer<String>>()
    every { observer.onChanged(any()) } returns Unit

    // When
    mainViewModel.observeText().observeForever(observer)

    // Then
    verify {
        observer.onChanged("Plouf")
    }
}

// Test to check the state after a click
@Test
fun checkTextAfterClick() {
    // Given
    val observer = mockk<Observer<String>>()
    every { observer.onChanged(any()) } returns Unit

    // When
    mainViewModel.observeText().observeForever(observer)
    mainViewModel.updateText()

    // Then
    verify {
        observer.onChanged("Plaf")
    }
}

}

And there is my stacktrace:

io.mockk.MockKException: No matching constructors found: constructor(application : android.app.Application = )

at io.mockk.impl.annotations.MockInjector.constructorInjection(MockInjector.kt:20) at io.mockk.impl.annotations.JvmMockInitializer.doInjection(JvmMockInitializer.kt:86) at io.mockk.impl.annotations.JvmMockInitializer.initMock(JvmMockInitializer.kt:64) at io.mockk.impl.annotations.JvmMockInitializer.initAnnotatedMocks(JvmMockInitializer.kt:22) at com.example.viewmodeltests.MainViewModelTests.before(MainViewModelTests.kt:88) 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.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24) at org.mockito.internal.junit.JUnitRule$1.evaluateSafely(JUnitRule.java:52) at org.mockito.internal.junit.JUnitRule$1.evaluate(JUnitRule.java:43) at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55) at toothpick.testing.ToothPickStatement.evaluate(ToothPickStatement.java:17) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) 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)

If succeded to do some tests on my use case but i failed on the viewModel. I don't understand why. Do you have an idea ?

Thank's

Claire

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/stephanenicolas/toothpick/issues/333?email_source=notifications&email_token=ABSAEWIVQNPXDC7U5DHSJ3TQCLIV3A5CNFSM4HSIUAYKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD3KNBJY#issuecomment-517263527, or mute the thread https://github.com/notifications/unsubscribe-auth/ABSAEWKFKCES47QJY2MSNDLQCLIV3ANCNFSM4HSIUAYA .

Claire1817 commented 5 years ago

effectively, i deleted it !

dlemures commented 5 years ago

Hi @ClaireGizard ,

Sorry for the late response, we have been quite busy releasing the 3rd version.

The error you are having is with mockK. It is trying to find a mock of Application to inject in the MainViewModel constructor, but you haven't defined any:

class MainViewModelTests {

    @get:Rule
    var toothPickRule = ToothPickRule(this, "test scope")

    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()

    @InjectMockKs  // <- USING MOCKK TO INJECT THE DEPENDENCIES
    lateinit var mainViewModel: MainViewModel 

    @Before
    fun before() {
        MockKAnnotations.init(this)  // <- YOU DONT HAVE ANY MOCK ANNOTATION
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
        toothPickRule.inject(this)  // <- DOING NOTHING
    }
}

Even though you are using toothPickRule.inject(this), you are not using TP to inject MainViewModel. You are using mockK instead. You need to use one or the other.

I've not used mockK before, but if you decide to with that option, I think that something like this should work:

class MainViewModelTests {

    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()

    @MockK
    lateinit var application: Application

    @InjectMockKs
    lateinit var mainViewModel: MainViewModel 

    @Before
    fun before() {
        MockKAnnotations.init(this)
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
    }
}