Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KoinAppAlreadyStartedException: A Koin Application has already been started

Using koin-2.0.1 for Android testing and unable to test all 3 test together though each test passes separately.

class NumberFormatterUtilImplTest : KoinTest {

    private val numberFormatterUtil: NumberFormatterUtilImpl by inject()

    @Before
    fun setUp() {
        startKoin { modules(utilsModule) }
    }

    @Test
    fun `does formatter returns two digit faction if supplied one digit value`() {
        val result = numberFormatterUtil.getAdjustedCurrencyRate(18.0)
        Assert.assertEquals(result, 18.00, 1.0)
    }

    @Test
    fun `does formatter returns two digit faction if supplied multiple digits value`() {
        val result = numberFormatterUtil.getAdjustedCurrencyRate(18.12343)
        Assert.assertEquals(result, 18.12, 1.0)
    }

    @Test
    fun `does formatter returns rounded two digit faction if supplied multiple digits value`() {
        val result = numberFormatterUtil.getAdjustedCurrencyRate(18.12876)
        Assert.assertEquals(result, 18.13, 1.0)
    }
}

running class level testing resulting below:

org.koin.core.error.KoinAppAlreadyStartedException: A Koin Application has already been started

any input would be helpful, thanks.

like image 970
Faisal Avatar asked Jul 15 '19 11:07

Faisal


3 Answers

A common practice is to pair @Before setup with @After cleanup. You can call stopKoin() there so the next call to startKoin() works again:

@After
fun tearDown() {
    stopKoin()
}
like image 94
laalto Avatar answered Nov 17 '22 13:11

laalto


Call stopKoin() on @Before and @After method like below:

import com.my.example.appModule
import android.os.Build
import androidx.test.core.app.ApplicationProvider
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.test.KoinTest
import org.koin.test.inject
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import java.util.*

@Config(sdk = [Build.VERSION_CODES.LOLLIPOP])
@RunWith(RobolectricTestRunner::class)
class SomeRepositoryTest: KoinTest {

    // Return Completable of RxJava
    private val repository: SomeRepository by inject()

    @Before
    fun before() {
        stopKoin() // to remove 'A Koin Application has already been started'
        startKoin {
            androidContext(ApplicationProvider.getApplicationContext())
            modules(appModule)
        }
    }

    @After
    fun after() {
        stopKoin()
    }

    @Test
    fun testSomething() {

        repository.insert("data").blockingAwait()

        assert(true)
    }
}
like image 7
BurtK Avatar answered Nov 17 '22 12:11

BurtK


As an alternative to the @After approach, you can also use AutoCloseKoinTest. As described in the docs:

Extended Koin Test - embed autoclose @after method to close Koin after every test

Instead of extending KoinTest, you can extend AutoCloseKoinTest and it will do the after test for you.

like image 12
LeoColman Avatar answered Nov 17 '22 14:11

LeoColman