docs/reference/koin-test/testing.md
Warning: This does not apply to Android Instrumented tests. For Instrumented testing with Koin, please see Android Instrumented Testing
By tagging your class KoinTest, your class become a KoinComponent and bring you:
by inject() & get() - function to retrieve your instances from Koinverify() - help you verify your module configurationdeclareMock & declare - to declare a mock or a new definition in the current contextclass ComponentA
class ComponentB(val a: ComponentA)
class MyTest : KoinTest {
// Lazy inject property
val componentB : ComponentB by inject()
@Test
fun `should inject my components`() {
startKoin {
modules(
module {
single { ComponentA() }
single { ComponentB(get()) }
})
}
// directly request an instance
val componentA = get<ComponentA>()
assertNotNull(a)
assertEquals(componentA, componentB.a)
}
:::note Don't hesitate to overload Koin modules configuration to help you partly build your app. :::
You can easily create and hold a Koin context for each of your test with the following rule:
@get:Rule
val koinTestRule = KoinTestRule.create {
// Your KoinApplication instance here
modules(myModule)
}
To let you use the declareMock API, you need to specify a rule to let Koin know how you build your Mock instance. This let you choose the right mocking framework for your need.
Create mocks using Mockito:
@get:Rule
val mockProvider = MockProviderRule.create { clazz ->
// Your way to build a Mock here
Mockito.mock(clazz.java)
}
Create mocks using MockK:
@get:Rule
val mockProvider = MockProviderRule.create { clazz ->
// Your way to build a Mock here
mockkClass(clazz)
}
!> koin-test project is not tied anymore to mockito
Instead of making a new module each time you need a mock, you can declare a mock on the fly with declareMock:
class ComponentA
class ComponentB(val a: ComponentA)
class MyTest : KoinTest {
@get:Rule
val koinTestRule = KoinTestRule.create {
modules(
module {
single { ComponentA() }
single { ComponentB(get()) }
})
}
@get:Rule
val mockProvider = MockProviderRule.create { clazz ->
Mockito.mock(clazz.java)
}
@Test
fun `should inject my components`() {
}
// Replace current definition by a Mock
val mock = declareMock<ComponentA>()
// retrieve mock, same as variable above
assertNotNull(get<ComponentA>())
// is built with mocked ComponentA
assertNotNull(get<ComponentB>())
}
:::note declareMock can specify if you want a single or factory, and if you want to have it in a module path. :::
When a mock is not enough and don't want to create a module just for this, you can use declare:
@Test
fun `successful declare an expression mock`() {
startKoin { }
declare {
factory { ComponentA("Test Params") }
}
Assert.assertNotEquals(get<ComponentA>(), get<ComponentA>())
}
Koin offers a way to test if your Koin modules are good: verify() - walk through your definition tree and check if each definition is bound.
@Test
fun checkKoinModules() {
myModule.verify()
}
:::info
The checkModules() API is deprecated. Use verify() instead. See Module Verification for details.
Both verification APIs will be replaced by native compile-time safety in the Koin Compiler Plugin. :::
Take attention to stop your koin instance (if you use startKoin in your tests) between every test. Else be sure to use koinApplication, for local koin instances or stopKoin() to stop the current global instance.
JUnit 5 support provides Extensions that will handle the starting and stopping of Koin context. This means that if you are using the extension you don't need to use the AutoCloseKoinTest.
For testing with JUnit5 you need to use koin-test-junit5 dependency.
You need to Register the KoinTestExtension and provide your module configuration. After this is done
you can either get or inject your components to the test. Remember to use @JvmField with the @RegisterExtension.
class ExtensionTests: KoinTest {
private val componentB by inject<Simple.ComponentB>()
@JvmField
@RegisterExtension
val koinTestExtension = KoinTestExtension.create {
modules(
module {
single { Simple.ComponentA() }
single { Simple.ComponentB(get()) }
})
}
@Test
fun contextIsCreatedForTheTest() {
Assertions.assertNotNull(get<Simple.ComponentA>())
Assertions.assertNotNull(componentB)
}
}
This works the same way as in JUnit4 except you need to use @RegisterExtension.
class MockExtensionTests: KoinTest {
val mock: Simple.UUIDComponent by inject()
@JvmField
@RegisterExtension
val koinTestExtension = KoinTestExtension.create {
modules(
module {
single { Simple.UUIDComponent() }
})
}
@JvmField
@RegisterExtension
val mockProvider = MockProviderExtension.create { clazz ->
Mockito.mock(clazz.java)
}
@Test
fun mockProviderTest() {
val uuidValue = "UUID"
declareMock<Simple.UUIDComponent> {
BDDMockito.given(getUUID()).will { uuidValue }
}
Assertions.assertEquals(uuidValue, mock.getUUID())
}
}
You can also get the created koin context as a function parameter. This can be achieved by adding a function parameter to the test function.
class ExtensionTests: KoinTest {
@RegisterExtension
@JvmField
val koinTestExtension = KoinTestExtension.create {
modules(
module {
single { Simple.ComponentA() }
})
}
@Test
fun contextIsCreatedForTheTest(koin: Koin) {
// get<SimpleComponentA>() == koin.get<Simple.ComponentA>()
Assertions.assertNotNull(koin.get<Simple.ComponentA>())
}
}