Skip to main content

CheckModules - Check Koin configuration (Deprecated)

danger

This API is now deprecated - since Koin 4.0

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

Koin Dynamic Check - CheckModules()

Invoke 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 allows to specify how to work with the following case:

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

  • withInstance<MyType>() - will add a mocked instance of MyType. Use MockProviderRule. (can be used in dependency 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 framework 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 an 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 a 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_",...) }
}
}
}

You can link scopes by using withScopeLink function incheckModules block to inject instances from another scope's definitions:

val myModule = module {
scope(named("scope1")) {
scoped { ComponentA() }
}
scope(named("scope2")) {
scoped { ComponentB(get()) }
}
}
@Test
fun `test DI modules`(){
koinApplication {
modules(myModule)
checkModules(){
withScopeLink(named("scope2"), named("scope1"))
}
}
}