vendredi 11 septembre 2020

Android Test Koin NoBeanDefFoundException

I'm trying to do some Android Tests with Koin and so far, it is not a success.

I want to test a basic Activity with a ViewModel, injected by Koin.

I already read posts like NoBeanDefFoundException with Mock ViewModel, testing with Koin, Espresso but so far I still have the error.


Here is the code relative to the tests configuration

A specific app that start with no module.

class MyTestApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin { emptyList<Module>() }
    }
}

A specific runner that uses the test app

class OccazioTestRunner : AndroidJUnitRunner() {
    override fun newApplication(
        cl: ClassLoader?,
        className: String?,
        context: Context?
    ): Application {
        return super.newApplication(cl, MyTestApplication::class.java.name, context)
    }
}

That is defined in my app build.gradle to be used as runner

android {
    defaultConfig {
       testInstrumentationRunner "fr.dsquad.occazio.occazio.OccazioTestRunner"
    }
}

And now the code I want to test

In my MyActivity

class MyActivity : AppCompatActivity(R.layout.activity_my) {

    private val myViewModel by viewModel<MyViewModel>()

    // Some code
}

And the viewmodel

class MyViewModel(private val useCase: MyUseCase): ViewModel() {
   // Some code
}


And finally, the test itself (in androidTest)

@LargeTest
class MyActivityTest : KoinTest {

    private lateinit var mockUseCase: MyUseCase

    @JvmField
    @Rule
    val activityRule = activityScenarioRule<MyActivity>()

    @Before
    fun setup() {
        mockUseCase = mock(MyUseCase::class.java)

        startKoin {
            modules(module { viewModel { MyViewModel(mockUseCase) } })
        }

        // I've also tried this
        loadKoinModules(
            module { viewModel { MyViewModel(mockUseCase) } }
        )
    }

    @After
    fun cleanUp() {
        stopKoin()
    }

    @Test
    fun someTest() = runBlocking {
        // Mock the usecase response
        `when`(mockUseCase.doSomething()).thenReturn("taratata")

        // Start the scenario
        val scenario = activityRule.scenario

        // Verify we call the getUserId
        // Activity is supposed to call the view model that will call the method doSomethingAdterThat.
        verify(mockUseCase, times(1)).doSomethingAfterThat()

        return@runBlocking
    }
}

And so far, everytime I run this code I have this error

org.koin.core.error.NoBeanDefFoundException: 
No definition found for 'mypackage.MyViewModel' has been found. Check your module definitions.

What is interesting is that, when

  1. I change the rule activityScenarioRule by the old deprecated ActivityTestRule(SplashScreenActivity::class.java, true, false)
  2. I change val scenario = activityRule.scenario by val scenario = activityRule.launchActivity(null)
  3. I use loadKoinModules and not startKoin in setUp

Two things happen

  1. When my test is started alone (via Android Studio): it passes.
  2. When my test is started with other tests (by the class or with connectedAndroidTest), only one of them passes and old the others are KO.

So I have two questions in fact here.

  1. How can I make this test work with activityScenarioRule ?
  2. How can I make them "all" work (and not start them one by one to make them work) ?

Aucun commentaire:

Enregistrer un commentaire