2

I would like to inject mocked overrides into my Android instrumentation tests using Kodein. I don't know which is the optimal approach to do this. Here's what I have in mind:

  • My app uses a KodeinAware application class. The served Kodein instance holds all dependencies required by my app.
  • In my tests I would like to inject mocked overrides for specific dependencies to test behavior of the app in various situations.
  • Overrides should be different for each test, and should be injected before/while the test runs.

Is the configurable Kodein extension sensible in this situation, or is there a simpler, better suited approach (and if so, which)?

david.schreiber
  • 3,851
  • 2
  • 28
  • 46

2 Answers2

5

If your test is given a Kodein instance (meaning that it can use a different Kodein object than the one held by your Application), then the recommended approach is to create a new Kodein object that extends the one of the app and overrides all necessary bindings.

val testKodein = Kodein {
    extend(appKodein())

    bind<MyManager>(overrides = true) with singleton { mock<MyManager>() }
}

The configurable Kodein option is recommended only if you're using a static "one true Kodein". Using it prevents the possibility to run you're tests in parallel (because they all access the same Kodein instance), and forces you to clear the ConfigurableKodein between each tests and re-declare every time different overrides.

Salomon BRYS
  • 9,247
  • 5
  • 29
  • 44
  • I'm using that approach in my unit tests, i.e. in tests that verify the correct behavior of injected classes. However, in my instrumentation, more specifically in my functional tests, I'm testing an instance of my real application. To make tests deterministic, I replace certain dependencies in my graph with mocks. Kodein is provided by the `Application` instance, hence it is like "the one true Kodein". I'm testing a configurable kodein approach right now, and will come back once I have some results. – david.schreiber Oct 13 '16 at 14:02
5

I am now using the ConfigurableKodein inside my custom App class.

class App : Application(), KodeinAware {
    override val kodein = ConfigurableKodein()

    override fun onCreate() {
        super.onCreate()

        // A function is used to create a Kodein module with all app deps.
        kodein.addImport(appDependencies(this))
    }
}

// Helper for accessing the App from any context.
fun Context.asApp() = this.applicationContext as App

Inside my AppTestRunner class, I declare the configuration to be mutable. That way I can reset it's configuration between each and every test.

class AppTestRunner : AndroidJUnitRunner() {
    override fun callApplicationOnCreate(app: Application) {
        app.asApp().kodein.mutable = true
        super.callApplicationOnCreate(app)
    }
}

I have created a JUnit rule that reset the dependency graph before every test.

class ResetKodeinRule : ExternalResource() {
    override fun before() {
        val app = InstrumentationRegistry.getInstrumentation().targetContext.asApp()
        app.kodein.clear()
        app.kodein.addImport(appDependencies(app))
    }
}

In my tests I can now retrieve the App.kodein instance and inject mocks that override dependencies of the original graph. The only thing that needs to be guaranteed is that the tested activity is launched after configuring mocks, or behavior is not predictable.

david.schreiber
  • 3,851
  • 2
  • 28
  • 46