android - Android 测试 Koin NoBeanDefFoundException
问题描述
我正在尝试使用 Koin 进行一些 Android 测试,但到目前为止,还没有成功。
我想用 Koin 注入的 ViewModel 测试一个基本的 Activity。
我已经阅读了类似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.
有趣的是,当
- 我改变
activityScenarioRule
了旧的不推荐使用的规则ActivityTestRule(SplashScreenActivity::class.java, true, false)
- 我
val scenario = activityRule.scenario
改变val scenario = activityRule.launchActivity(null)
- 我使用而
loadKoinModules
不是startKoin
setUp
发生了两件事
- 当我的测试单独开始时(通过 Android Studio):它通过了。
- 当我的测试开始与其他测试(通过类或使用 connectedAndroidTest)时,只有一个通过,而其他的都是 KO。
所以我实际上有两个问题。
- 我怎样才能使这个测试工作
activityScenarioRule
? - 我怎样才能让它们“全部”工作(而不是一一启动它们以使它们工作)?
解决方案
好的,不要问我它是如何工作的,但我想通了。
首先,因为我需要配置,所以我遵循了这个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 中的那个替换了这个loadKoinModules
块startKoin
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()
}
只是要确定 !
推荐阅读
- python - SciKit-Learn CustomTransformer:TypeError:'numpy.ndarray'对象不可调用
- scala - 在 SPARK SQL 中读取分区的 HIVE 表
- magento - Magento 2.3.2版如何更改tinymce js路径
- java - 错误:(13,28) java: com.aliasi.tokenizer 包不存在
- android - 无法解析“:capacitor-android@debug/compileClasspath”的依赖关系
- r - 在 x 轴上绘制两行中因子的交互作用
- php - 等效于 openssl_encrypt 的 mcrypt 加密
- ios - 持续位置更新后台iOS13
- javascript - liveChange 适用于表中的多个列?
- windows - Ionic - 使 Windows 应用程序全屏运行