7

I'm using "withTestAppliction" in one of my tests to test if the route works. Before all Tests the DB-Table "cats" should have no entries. To get the DAO I need Koin in this Test but if conflicts with "withTestAppliction" where Koin will also be startet and throws A KoinContext is already started

[Update]
I know I could use something like handleRequest(HttpMethod.Delete, "/cats") but I don't want to expose this Rest-Interface. Not even for testing.

@ExperimentalCoroutinesApi
class CatsTest: KoinTest {
    companion object {
        @BeforeClass
        @JvmStatic fun setup() {
            // once per run
            startKoin {
                modules(appModule)
            }
        }

        @AfterClass
        @JvmStatic fun teardown() {
            // clean up after this class, leave nothing dirty behind
            stopKoin()
        }
    }

    @Before
    fun setupTest() = runBlockingTest {
        val dao = inject<CatDAO>()
        dao.value.deleteAll()
    }

    @After
    fun cleanUp() {

    }

    @Test
    fun testCreateCat() {
        withTestApplication({ module(testing = true) }) {
            val call = createCat(predictName("Pepples"), 22)

            call.response.status().`should be`(HttpStatusCode.Created)
        }
    }

}

fun TestApplicationEngine.createCat(name: String, age: Int): TestApplicationCall {
    return handleRequest(HttpMethod.Post, "/cats") {
        addHeader(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded.toString())
        setBody(listOf(
                "name" to name,
                "age" to age.toString()
        ).formUrlEncode())
    }
}

Mike Mitterer
  • 6,810
  • 4
  • 41
  • 62
  • I just had the same problem and fixed it by changing the class definition from class Class : KoinTest {} to class Class : AutoCloseKoinTest {}. I'm not posting this as an answer yet because I don't know why it works. – Lorenzo Petroli Apr 30 '20 at 10:02

3 Answers3

2

After test (after withTestApplication()) call KoinContextHandler.get().stopKoin().

Example: https://github.com/comm1x/ktor-boot/blob/master/test/common/common.kt

Pavel Shorokhov
  • 4,485
  • 1
  • 35
  • 44
0

It looks similar to the issue I faced. The problem was that the module() passed under the withTestApplication() was trying to create the Koin object again. I replaced the module() with specific modules that I had to load for the tests except for the Koin.

Refer - test sample and application sample

Sahil Chhabra
  • 10,621
  • 4
  • 63
  • 62
0

Had the same problem executing multiple tests in a class. After removing the init/startKoin (since it's initialized in the Application when you test with the emulator). I am not really sure if this is the correct approach, but it kind of works for me and my build server.

@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4ClassRunner::class) // or JUnit4..
class MyTest : KoinTest {

    private val mockedAppModule: Module = module(override = true)
        factory { myRepo }
    }

    @Before
    fun setup() {
        loadKoinModules(mockedAppModule)
    }

    @After
    fun tearDown() {
        unloadKoinModules(mockedAppModule)
    }

    @Test
    fun testSubscriberRegistration() = runBlockingTest { // only needed if you are using supend functions
         // test impl...
    }
}
Javatar
  • 2,518
  • 1
  • 31
  • 43