Skip to main content

Test

When writing test code, it is recommended to use actual dependencies as much as possible rather than mocks. By using Koject, you can easily replace only some dependencies and test them.

Setup for Testing

To use Koject in tests, add koject-test to the test dependencies and register koject-processor-app to the test source set.

Multiplatform

build.gradle.kts
kotlin {
/* ... */

sourceSets {
val commonMain by getting {
dependencies {
implementation("com.moriatsushi.koject:koject-core:1.3.0")
}
}

val commonTest by getting {
dependencies {
implementation(kotlin("test"))
+ implementation("com.moriatsushi.koject:koject-test:1.3.0")
}
}
}
}


dependencies {
// Add it according to your targets.
val processor = "com.moriatsushi.koject:koject-processor-app:1.3.0"
add("kspAndroid", processor)
add("kspJvm", processor)
add("kspJs", processor)
add("kspIosX64", processor)
add("kspIosArm64", processor)

+ add("kspAndroidTest", processor)
+ add("kspJvmTest", processor)
+ add("kspJsTest", processor)
+ add("kspIosX64Test", processor)
+ add("kspIosArm64Test", processor)
}

Single platform

build.gradle.kts
dependencies {
implementation("com.moriatsushi.koject:koject-core:1.3.0")

testImplementation(kotlin("test"))
+ testImplementation("com.moriatsushi.koject:koject-test:1.3.0")
+ kspTest("com.moriatsushi.koject:koject-processor-app:1.3.0")
}

Running tests

Using Koject.runTest(), you can start the test DI container and use the provided dependencies.

@Provides
class Controller
class SampleTest() {
@Test
fun test() = Koject.runTest {
val controller = inject<Controller>() // can be injected
}
}

You can also start a test DI container by executing Koject.startTest() in @Before and Koject.stop() in @After.

class SampleTest() {
@Before
fun start() {
Koject.startTest()
}

@After
fun stop() {
Koject.stop()
}

@Test
fun test() {
val controller = inject<Controller>()
}
}

Swapping dependencies in tests

You can use @TestProvides to replace dependencies during testing.

For example, suppose you provided and used SampleRepository as follows:

src/main/kotlin/SampleRepository.kt
interface SampleRepository

@Provide
@Binds
class SampleRepositoryImpl: SampleRepository
src/main/kotlin/main.kt
fun main() {
Koject.start()

val repository = inject<SampleRepository>() // is SampleRepositoryImpl
}

During testing, you may want to use different code because you do not want to communicate with the backend server or you want to test more efficiently. By using @TestProvides as follows, FakeSampleRepository is used during testing instead of SampleRepositoryImpl.

src/test/kotlin/FakeSampleRepository.kt
@TestProvides
@Binds
class FakeSampleRepository: SampleRepository
src/test/kotlin/FakeSampleRepository.kt
class SampleTest() {
@Test
fun test() = Koject.runTest {
val repository = inject<SampleRepository>() // is FakeSampleRepository
}
}

Note that FakeSampleRepository is not provided. You can provide both FakeSampleRepository and SampleRepository by using @TestProvides as follows.

src/test/kotlin/FakeSampleRepository.kt
@TestProvides
class FakeSampleRepository: SampleRepository {
companion object {
@TestProvides
fun bindAsSampleRepository(
fake: FakeSampleRepository
): SampleRepository {
return fake
}
}
}
src/test/kotlin/FakeSampleRepository.kt
class SampleTest() {
@Test
fun test() = Koject.runTest {
val fakeRepository = inject<FakeSampleRepository>() // is FakeSampleRepository
val repository = inject<SampleRepository>() // is FakeSampleRepository
}
}