KoinAppAlreadyStartedException:一个 Koin 应用程序已经启动

Posted

技术标签:

【中文标题】KoinAppAlreadyStartedException:一个 Koin 应用程序已经启动【英文标题】:KoinAppAlreadyStartedException: A Koin Application has already been started 【发布时间】:2019-11-24 02:28:53 【问题描述】:

使用 koin-2.0.1 进行 android 测试,虽然每个测试都单独通过,但无法同时测试所有 3 个测试。

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)
    

运行类级别测试结果如下:

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

任何意见都会有所帮助,谢谢。

【问题讨论】:

【参考方案1】:

一种常见的做法是将@Before setup 与@After cleanup 配对。您可以在那里拨打stopKoin(),以便下次拨打startKoin() 可以再次使用:

@After
fun tearDown() 
    stopKoin()

【讨论】:

或者,@BeforeClass/@AfterClass 扩展 AutoCloseKoinTest 是一种更直接和简洁的方法,因为您不需要手动编写 @After 设置来停止 koin 对我不起作用,必须这样做if (KoinContextHandler.getOrNull() == null) startKoin...【参考方案2】:

作为@After 方法的替代方法,您还可以使用AutoCloseKoinTest。 As described in the docs:

扩展 Koin 测试 - 嵌入 autoclose @after 方法以在每次测试后关闭 Koin

您可以扩展AutoCloseKoinTest,而不是扩展KoinTest,它会为您完成后续测试。

【讨论】:

链接无效。尝试了 @After 和 AutoCloseKoinTest 并且没有一个解决方案有效。不断收到同样的错误 对于 JUnit 5:doc.insert-koin.io/#/koin-test/testing?id=testing-with-junit5【参考方案3】:

在@Before 和@After 方法上调用 stopKoin(),如下所示:

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)
    

【讨论】:

【参考方案4】:

解决此问题的另一种方法是在您启动 Koin 的应用程序中覆盖 onTerminate

override fun onTerminate() 
    super.onTerminate()     
    stopKoin()              

通过这种方式,您将不必使用AutoCloseKoinTest 或在@after 中的每个类测试中关闭它

【讨论】:

【参考方案5】:

实现抽象 AutoCloseKoinTest 类将在每次测试后自动停止 Koin。

以下是 Robolectric 的示例:

@RunWith(RoboelectricTestRunner::class)
class MyTest : AutoCloseKoinTest() 
   private val appContext = ApplicationProvider.getApplicationContext<APPNAME>()

   @Before
   fun setup() 
      // use appContext as needed
   

【讨论】:

以上是关于KoinAppAlreadyStartedException:一个 Koin 应用程序已经启动的主要内容,如果未能解决你的问题,请参考以下文章