问题描述
我正在尝试使用Koin进行一些Android测试,到目前为止,这并不成功。
我想用由Koin注入的viewmodel测试基本活动。
我已经读过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)
}
}
在我的应用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
发生两件事
所以我实际上有两个问题。
- 如何使此测试适用于
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未启动” ...我只是在setUp中将loadKoinModules
替换为startKoin
startKoin { modules(module { viewModel { MyViewModel(mockUseCase) } }) }
最后,它可以进行1次测试,但其他失败,因为未调用stopKoin()
之类的“ KoinAppAlreadyStartedException”。 So I found out that I should extend AutoCloseKoinTest
instead of 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
}
}
Ho,我还将此代码添加到了我的两个Applications
override fun onTerminate() {
super.onTerminate()
stopKoin()
}
请确定!