1

I'm unable to use Koin 2.0.1 with Kotlin-test 3.4.2. I get an InvocationTargetException like this:

Running koinexample.KoinSampleTests
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.009 sec <<< FAILURE! - in koinexample.KoinSampleTests
koinexample.KoinSampleTests  Time elapsed: 0.009 sec  <<< ERROR!
java.lang.reflect.InvocationTargetException
        at koinexample.KoinSampleTests.getKoin(KoinSampleTests.kt:26)
        at koinexample.KoinSampleTests.<init>(KoinSampleTests.kt:61)

I've created a small example on GitHub that reproduces this error: https://github.com/elifarley/kotlin-tests-with-koin-examples

Just execute these commands to clone the repo and run tests:

git clone https://github.com/elifarley/kotlin-tests-with-koin-examples.git
cd kotlin-tests-with-koin-examples
mvn

Here's the main Kotlin file:

package koinexample

import io.kotlintest.koin.KoinListener
import io.kotlintest.shouldBe
import io.kotlintest.specs.FreeSpec
import org.koin.core.inject
import org.koin.dsl.module
import org.koin.test.KoinTest

data class Stats(var ok: Long = 0, var error: Long = 0)

interface StatsServer {
    fun newError(): Long
}

class StatsServerSimple(private val stats: Stats) : StatsServer {
    override fun newError() = stats.error++
}

val appModule = module {
    single { Stats() }
    single { StatsServerSimple(get()) as StatsServer }
}

class KoinSampleTests : FreeSpec(), KoinTest {

    private val modules = listOf(
        appModule
    )

    override fun listeners() = listOf(KoinListener(modules))

    val statsServer: StatsServer by inject()

    init {

        "Happy path" {
            statsServer.newError() shouldBe 1
        }
    }
}
LeoColman
  • 6,950
  • 7
  • 34
  • 63
Elifarley
  • 1,310
  • 3
  • 16
  • 23
  • 1
    This should NOT be closed as "It's caused by a typo or problem that can no longer be reproduced", because although it's a simple import error, this is a very common mistake made by Koin users, and thus having an answer for this issue is helpful – LeoColman Mar 06 '20 at 12:22

2 Answers2

1

Your issue seems to be a simple import confusion.

Note that you're using import org.koin.core.inject, which is this function:

inline fun <reified T> KoinComponent.inject(
        qualifier: Qualifier? = null,
        noinline parameters: ParametersDefinition? = null
): Lazy<T> =
        getKoin().inject(qualifier, parameters)

It needs getKoin to work, and therefore you cannot initialize your test (The test class is initialized before Koin had a chance to start with the listener).

The correct import is import org.koin.test.inject, which translates to:

inline fun <reified T> KoinTest.inject(
    qualifier: Qualifier? = null,
    noinline parameters: ParametersDefinition? = null
): Lazy<T> = lazy { get<T>(qualifier, parameters) }

Take note that this is indeed lazy, so Kotest will have a chance to initialize Koin before your tests start.

Fixing that import should resolve this issue

LeoColman
  • 6,950
  • 7
  • 34
  • 63
  • 1
    This is also fixed for Koin 2.1.3, so if you update your dependency, it won't matter from where you get the `inject` extension. – LeoColman Mar 06 '20 at 12:24
-1

Seems like you're never starting the Koin app. You need to have

startKoin {
            modules(appModule)
        }

in your test method or in the beforeSpec/beforeTest function call of the spec.

Or use something like here:

    override fun listeners() = listOf(KoinListener(appModule))

which is described in the documentation for kotlintest/kotest: https://github.com/Kotest/Kotest/blob/master/doc/extensions.md#koin

redspider
  • 401
  • 1
  • 6
  • 21