我正在尝试用Koin做一些Android测试,到目前为止,还不成功。
我想用Koin注入的ViewModel测试一个基本的activity。
我已经读了一些帖子,比如NoBeanDefFoundException with Mock ViewModel,testing with 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)
}
}
它在我的appbuild.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
}
和viewmodel
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.LaunchActivity(null)
VAL scenario=ActivityRule.scenario
setup
loadkoinmodules
而不是startkoin
会发生两件事
所以我有两个问题。
ActivityScenariorule
一起工作?
好吧,别问我它是怎么运作的,但我知道了。
首先,由于需要配置,我遵循了以下内容:https://medium.com/stepstone-tech/better-tests-with-androidxs-activityscenario-in-kotlin-part-1-6a6376b713ea。
我做了三件事
首先,我需要在启动之前配置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没有启动”……在安装程序中,我刚刚用startkoin
块替换了loadkoinmodules
块
startKoin { modules(module { viewModel { MyViewModel(mockUseCase) } }) }
最后,它对一个测试有效,但其他测试都失败了,因为没有调用“KoInAppalreadyStartedException”,比如StopKoin()
。所以我发现我应该扩展autoclosekointest
而不是kointest
。但没有成功。最后,我在startkoin
之前加上了一个stopkoin()
,现在,一切都像魅力一样工作了。
下面是我的完整代码
@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
}
}
Ho,我还将此代码添加到了我的两个应用程序
中
override fun onTerminate() {
super.onTerminate()
stopKoin()
}
只是为了确定!