Checking your modules or application graph

note

Koin allows you to verify your configuration modules, avoiding to discover dependency injection issues at runtime.

To verify your modules, you just need to the checkModules() function within a simple JUnit test. This will launch your modules and try to run each possible definition for you.

class CheckModulesTest : KoinTest {
@Test
fun verifyKoinApp() {
koinApplication {
modules(module1,module2)
checkModules()
}
}
}

also possible to use checkKoinModules:

class CheckModulesTest : KoinTest {
@Test
fun verifyKoinApp() {
checkKoinModules(listOf(module1,module2))
}
}

CheckModule DSL

For any definition that is using injected parameters, properties or dynamic instances, the checkModules DSL allow to specify how to wotk with the following case:

  • withInstance(value) - will add value instance to Koin graph (can be used in depednency or parameter)

  • withInstance<MyType>() - will add a mocked instance of MyType. Use MockProviderRule. (can be used in depednency or parameter)

  • withParameter<Type>(qualifier){ qualifier -> value } - will add value instance to be injected as parameter

  • withParameter<Type>(qualifier){ qualifier -> parametersOf(...) } - will add value instance to be injected as parameter

  • withProperty(key,value) - add property to Koin

Allow mocking with a Junit rule

To use mocks with checkModules, you need to provide a MockProviderRule

@get:Rule
val mockProvider = MockProviderRule.create { clazz ->
// Mock with your framwork here given clazz
}

Verifying modules with dynamic behavior (3.1.3+)

To verify a dynamic behavior like following, let's use the CheckKoinModules DSL to provide the missing instance data to our test:

val myModule = module {
factory { (id: String) -> FactoryPresenter(id) }
}

You can verify it with the following:

class CheckModulesTest : KoinTest {
@Test
fun verifyKoinApp() {
koinApplication {
modules(myModule)
checkModules(){
// value to add to Koin, used by definition
withInstance("_my_id_value")
}
}
}
}

This way, FactoryPresenter definition will be injected with "_my_id_value" define above.

You can also use mocked instances, to fill up your graph. You can notice that we need a MockProviderRule declaration to allow Koin mock any injected definition

val myModule1 = module {
factory { (a : ComponentA) -> ComponentB(a) }
}
// or
val myModule2 = module {
factory { ComponentB(get()) }
}
class CheckModulesTest : KoinTest {
@get:Rule
val mockProvider = MockProviderRule.create { clazz ->
// Setup your nock framework
Mockito.mock(clazz.java)
}
@Test
fun verifyKoinApp() {
koinApplication {
modules(myModule1)
checkModules(){
// add a mock of ComponentA to Koin
withInstance<ComponentA>()
}
}
}
}

Checking Modules for Android (3.1.3)

Here below is how you can test your graph for a typical Android app:

class CheckModulesTest {
@get:Rule
val rule: TestRule = InstantTaskExecutorRule()
@get:Rule
val mockProvider = MockProviderRule.create { clazz ->
Mockito.mock(clazz.java)
}
@Test
fun `test DI modules`(){
koinApplication {
modules(allModules)
checkModules(){
withInstance<Context>()
withInstance<Application>()
withInstance<SavedStateHandle>()
withInstance<WorkerParameters>()
}
}
}
}

also possible to use checkKoinModules:

class CheckModulesTest {
@get:Rule
val rule: TestRule = InstantTaskExecutorRule()
@get:Rule
val mockProvider = MockProviderRule.create { clazz ->
Mockito.mock(clazz.java)
}
@Test
fun `test DI modules`(){
checkKoinModules(allModules) {
withInstance<Context>()
withInstance<Application>()
withInstance<SavedStateHandle>()
withInstance<WorkerParameters>()
}
}
}

Providing Default Values (3.1.4)

If you need, you can set a default value for all type in the checked modules. For example, We can override all injected string values:

Let's use the withInstance() function in checkModules block, to define a default value for all definitions:

@Test
fun `test DI modules`(){
koinApplication {
modules(allModules)
checkModules(){
withInstance("_ID_")
}
}
}

All injected definition that are using a injected String parameter, will receive "_ID_":

module {
single { (i: String) -> Simple.ComponentC(i) }
factory { (id: String) -> FactoryPresenter(id) }
}

Providing ParametersOf values (3.1.4)

You can define default value to be injected for one specific definition, with withParameter or withParameters functions:

@Test
fun `test DI modules`(){
koinApplication {
modules(allModules)
checkModules(){
withParameter<FactoryPresenter> { "_FactoryId_" }
withParameters<FactoryPresenter> { parametersOf("_FactoryId_",...) }
}
}
}