ViewModel单元测试在一起运行时失败,但在单独运行时通过



我正在从ViewModel测试一个挂起的方法,该方法在协程完成时触发LiveData发出对象。什么时候我单独运行这些测试中的每一个,它们都通过了,当我一起运行它们时,第一个测试总是失败。令人惊讶的是,当我在调试中运行它们时,我在assertValue上设置了断点来检查vaule是什么,两个测试都通过了。我猜测问题出在LiveData或整个PaymentViewModel的状态上。我做错了什么?

class PaymentViewModelTest : KoinTest {
private val paymentViewModel : PaymentViewModel by inject()
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
private val mainThreadSurrogate = newSingleThreadContext("UI thread")
@Before
fun setup(){
Dispatchers.setMain(mainThreadSurrogate)
val modules = KoinModule()
startKoin {
androidContext(mock(Application::class.java))
modules(listOf(
modules.repositoryModule,
modules.businessModule,
modules.utilsModule)
)
}
declareMock<AnalyticsHelper>()
declareMock<Printer>()
}
@After
fun after(){
stopKoin()
Dispatchers.resetMain()
}
@Test
fun successfully_initializes_payment_flow() {
declareMock<PaymentRepository> {
runBlockingTest {
given(initPayment())
.willAnswer { InitPaymentResponse(0, PaymentStatus.INITIALIZED, 0) }
}
}
paymentViewModel.initPayment(BigDecimal(0))
paymentViewModel.paymentStatus.test()
.awaitValue()
.assertValue { value -> value.getContentIfNotHandled()?.data == PaymentStatus.INITIALIZED }
}
@Test
fun fails_to_initialize_payment_flow() {
declareMock<PaymentRepository> {
runBlockingTest {
given(initPayment())
.willThrow(MockitoKotlinException("", ConnectException()))
}
}
paymentViewModel.initPayment(BigDecimal(0))
paymentViewModel.paymentStatus.test()
.awaitValue()
.assertValue { value -> value.getContentIfNotHandled()?.status == ApiResponseStatus.ERROR}
}  
}

这是我正在测试的方法:

fun initPayment(price: BigDecimal) {
paymentStatus.postValue(Event(ApiResponse.loading()))
viewModelScope.launch {
runCatching {
repository.initPayment()
}.onSuccess {
paymentSession = PaymentSession(it.paymentId)
paymentSession.price = price
postPaymentStatus(it.status)
}.onFailure {
postApiError(it)
}
}
}
private fun postPaymentStatus(status: PaymentStatus) =
paymentStatus.postValue(Event(ApiResponse.success(status)))

这可能不是一个完整的答案,因为你的问题太多了。首先尝试使用CoroutineTestRule:

@ExperimentalCoroutinesApi
class CoroutineTestRule(
private val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
) : TestWatcher() {
override fun starting(description: Description?) {
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description?) {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
}

你的测试将类似于:

class PaymentViewModelTest : KoinTest {
private val paymentViewModel : PaymentViewModel by inject()
@get:Rule
val coroutineTestRule = CoroutineTestRule()
@Before
fun setup(){
startKoin {
androidContext(mock(Application::class.java))
modules(
modules.repositoryModule,
modules.businessModule,
modules.utilsModule
)
}
declareMock<AnalyticsHelper>()
declareMock<Printer>()
}
@After
fun after(){
stopKoin()
}
// Other methods are the same.
}

您可以使用AutoCloseKoinTest来删除after((方法。

当你孤立地运行测试时,你说测试通过了,所以也许这就足够了。但如果这不起作用,还有更多需要挖掘的地方。例如,我觉得奇怪的是,您在mock中使用runBlockingTest,而断言在该块之外。通常我会使用MockK来模拟挂起函数,并在runBlockingTest中测试和断言其中的任何一个。

相关内容

最新更新