Koin - 如何为浓缩咖啡测试提供模拟 ViewModel?
Koin - how to provide mock ViewModel for espresso test?
我们如何将模拟的 viewModel 注入 Activity 以进行浓缩咖啡测试?
使用 declareMock
我在 Test class 中得到了模拟对象,但是 Activity 收到了常规的 viewModel 对象。
@RunWith(AndroidJUnit4::class)
class SomeActivityTest : KoinTest {
@Rule
@JvmField
val rule = ActivityTestRule(SomeActivity::class.java, true, true)
val viewModel: MyViewModel by inject()
@Before
fun setup() {
declareMock<MyViewModel>(isFactory = true, binds = listOf(ViewModel::class))
}
@After
fun cleanUp() {
stopKoin()
}
@Test
fun shouldHaveTextViewVisible() {
`when`(viewModel.sayHello())
.thenReturn("hello view-model")
onView(withId(R.id.tv_homescreen_message))
.check(matches(isDisplayed()))
onView(withId(R.id.tv_homescreen_message))
.check(matches(withText("hello view-model")))
}
}
在这种情况下,Espresso 测试仍在使用主应用程序 class,它声明了应用程序所需的所有 koin
模块。
在没有任何模块的情况下启动koin
,允许我们在测试期间仅加载所需的模块。
// application class for espresso tests
class TestApp : Application() {
override fun onCreate() {
super.onCreate()
startKoin(this, emptyList())
}
}
class TestAppJUnitRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, TestApp::class.java.name, context)
}
}
// app module build.gradle
android {
defaultConfig {
testInstrumentationRunner "com.package.TestAppJUnitRunner"
}
}
在开始之前声明模拟方法很重要activity
@RunWith(AndroidJUnit4::class)
class SomeActivityTest : KoinTest {
@Rule
@JvmField
val rule = ActivityTestRule(SomeActivity::class.java, true, false)
lateinit var mockVm: MyViewModel
@Before
fun setup() {
mockVm = mock(MyViewModel::class.java)
loadKoinModules(module {
viewModel {
mockVm
}
})
}
@After
fun cleanUp() {
stopKoin()
}
@Test
fun shouldHaveTextViewWithMessage() {
// 1. declare mock method
val message = "hello view-model"
Mockito.`when`(mockVm.sayHello())
.thenReturn(message)
// 2. start activity
rule.launchActivity(null)
// 3. test
onView(withId(R.id.tv_message))
.check(matches(isDisplayed()))
onView(withId(R.id.tv_message))
.check(matches(withText(message)))
}
}
我们如何将模拟的 viewModel 注入 Activity 以进行浓缩咖啡测试?
使用 declareMock
我在 Test class 中得到了模拟对象,但是 Activity 收到了常规的 viewModel 对象。
@RunWith(AndroidJUnit4::class)
class SomeActivityTest : KoinTest {
@Rule
@JvmField
val rule = ActivityTestRule(SomeActivity::class.java, true, true)
val viewModel: MyViewModel by inject()
@Before
fun setup() {
declareMock<MyViewModel>(isFactory = true, binds = listOf(ViewModel::class))
}
@After
fun cleanUp() {
stopKoin()
}
@Test
fun shouldHaveTextViewVisible() {
`when`(viewModel.sayHello())
.thenReturn("hello view-model")
onView(withId(R.id.tv_homescreen_message))
.check(matches(isDisplayed()))
onView(withId(R.id.tv_homescreen_message))
.check(matches(withText("hello view-model")))
}
}
在这种情况下,Espresso 测试仍在使用主应用程序 class,它声明了应用程序所需的所有 koin
模块。
在没有任何模块的情况下启动koin
,允许我们在测试期间仅加载所需的模块。
// application class for espresso tests
class TestApp : Application() {
override fun onCreate() {
super.onCreate()
startKoin(this, emptyList())
}
}
class TestAppJUnitRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, TestApp::class.java.name, context)
}
}
// app module build.gradle
android {
defaultConfig {
testInstrumentationRunner "com.package.TestAppJUnitRunner"
}
}
在开始之前声明模拟方法很重要activity
@RunWith(AndroidJUnit4::class)
class SomeActivityTest : KoinTest {
@Rule
@JvmField
val rule = ActivityTestRule(SomeActivity::class.java, true, false)
lateinit var mockVm: MyViewModel
@Before
fun setup() {
mockVm = mock(MyViewModel::class.java)
loadKoinModules(module {
viewModel {
mockVm
}
})
}
@After
fun cleanUp() {
stopKoin()
}
@Test
fun shouldHaveTextViewWithMessage() {
// 1. declare mock method
val message = "hello view-model"
Mockito.`when`(mockVm.sayHello())
.thenReturn(message)
// 2. start activity
rule.launchActivity(null)
// 3. test
onView(withId(R.id.tv_message))
.check(matches(isDisplayed()))
onView(withId(R.id.tv_message))
.check(matches(withText(message)))
}
}