簡體   English   中英

Android 測試 Koin NoBeanDefFoundException

[英]Android Test Koin NoBeanDefFoundException

我正在嘗試使用 Koin 進行一些 Android 測試,但到目前為止,還沒有成功。

我想用 Koin 注入的 ViewModel 測試基本活動。

我已經閱讀了類似NoBeanDefFoundException 和 Mock ViewModel 的帖子,用 Koin、Espresso 進行測試,但到目前為止我仍然有錯誤。


這是與測試配置相關的代碼

一個沒有模塊的特定應用程序。

class MyTestApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin { emptyList<Module>() }
    }
}

使用測試應用程序的特定運行程序

class OccazioTestRunner : AndroidJUnitRunner() {
    override fun newApplication(
        cl: ClassLoader?,
        className: String?,
        context: Context?
    ): Application {
        return super.newApplication(cl, MyTestApplication::class.java.name, context)
    }
}

這是在我的應用程序build.gradle定義的用作跑步者

android {
    defaultConfig {
       testInstrumentationRunner "fr.dsquad.occazio.occazio.OccazioTestRunner"
    }
}

現在我想測試的代碼

在我的MyActivity

class MyActivity : AppCompatActivity(R.layout.activity_my) {

    private val myViewModel by viewModel<MyViewModel>()

    // Some code
}

和視圖模型

class MyViewModel(private val useCase: MyUseCase): ViewModel() {
   // Some code
}


最后,測試本身(在 androidTest 中)

@LargeTest
class MyActivityTest : KoinTest {

    private lateinit var mockUseCase: MyUseCase

    @JvmField
    @Rule
    val activityRule = activityScenarioRule<MyActivity>()

    @Before
    fun setup() {
        mockUseCase = mock(MyUseCase::class.java)

        startKoin {
            modules(module { viewModel { MyViewModel(mockUseCase) } })
        }

        // I've also tried this
        loadKoinModules(
            module { viewModel { MyViewModel(mockUseCase) } }
        )
    }

    @After
    fun cleanUp() {
        stopKoin()
    }

    @Test
    fun someTest() = runBlocking {
        // Mock the usecase response
        `when`(mockUseCase.doSomething()).thenReturn("taratata")

        // Start the scenario
        val scenario = activityRule.scenario

        // Verify we call the getUserId
        // Activity is supposed to call the view model that will call the method doSomethingAdterThat.
        verify(mockUseCase, times(1)).doSomethingAfterThat()

        return@runBlocking
    }
}

到目前為止,每次運行此代碼時都會出現此錯誤

org.koin.core.error.NoBeanDefFoundException: 
No definition found for 'mypackage.MyViewModel' has been found. Check your module definitions.

有趣的是,當

  1. 我通過舊的不推薦使用的ActivityTestRule(SplashScreenActivity::class.java, true, false)更改規則activityScenarioRule
  2. 我將val scenario = activityRule.scenario更改為val scenario = activityRule.launchActivity(null)
  3. 我在setUp使用loadKoinModules而不是startKoin

發生兩件事

  1. 當我的測試單獨開始時(通過 Android Studio):它通過了。
  2. 當我的測試與其他測試(通過類或使用 connectedAndroidTest)一起開始時,只有其中一個通過,而其他測試則為 KO。

所以我在這里實際上有兩個問題。

  1. 我怎樣才能使這個測試與activityScenarioRule工作?
  2. 我怎樣才能讓它們“全部”工作(而不是一一啟動它們以使其工作)?

好吧,不要問我它是如何工作的,但我想通了。

首先,因為我需要配置,所以我遵循了這個https://medium.com/stepstone-tech/better-tests-with-androidxs-activityscenario-in-kotlin-part-1-6a6376b713ea

我做了3件事

首先,我需要在啟動前配置 koin,為此,我需要使用ActivityScenario.launch()和我之前定義的意圖

private val intent = Intent(ApplicationProvider.getApplicationContext(), MyActivity::class.java)
var activityRule : ActivityScenario<MyActivity>? = null

// And then I can start my activity calling
activityRule = ActivityScenario.launch(intent)

然后“KoinApp 沒有啟動”……我只是用 setUp 中的startKoin替換了loadKoinModules

startKoin { modules(module { viewModel { MyViewModel(mockUseCase) } }) }

最后,它適用於 1 個測試,但其他測試失敗,因為沒有調用像stopKoin()這樣的“KoinAppAlreadyStartedException”。 所以我發現我應該擴展AutoCloseKoinTest而不是KoinTest .. 但沒有成功。 最后,我已經把stopKoin()的前startKoin而現在,一切工作就像一個魅力。

這是我的完整代碼

@LargeTest
class MyActivityTest : KoinTest() {

    private val intent = Intent(ApplicationProvider.getApplicationContext(), MyActivity::class.java)
    var activityRule : ActivityScenario<MyActivity>? = null

    private lateinit var mockUseCase: MyUseCase

    @Before
    fun setup() {
        mockUseCase = mock(MyUseCase::class.java)
        stopKoin()
        startKoin {
            modules(module { viewModel { MyViewModel(mockUseCase) } })
        }
    }

    @After
    fun cleanUp() {
        activityRule?.close()
    }

    @Test
    fun someTest() = runBlocking {
        // Mock the usecase response
        `when`(mockUseCase.doSomething()).thenReturn("taratata")

        // Start the rule
        val activityRule = ActivityScenario.launch(intent)

        // Verify we call the getUserId
        // Activity is supposed to call the view model that will call the method doSomethingAdterThat.
        verify(mockUseCase, times(1)).doSomethingAfterThat()

        return@runBlocking
    }

}

何,我也將此代碼添加到我的兩個Applications

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

只是要確定 !

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM