Unit testing LiveData单元测试在协程和多线程下无法通过,因为返回true而不是预期的false
EnrollmentViewModelTestUnit testing LiveData单元测试在协程和多线程下无法通过,因为返回true而不是预期的false,unit-testing,kotlin,android-architecture-components,android-livedata,coroutine,Unit Testing,Kotlin,Android Architecture Components,Android Livedata,Coroutine,EnrollmentViewModelTest @Test fun getUserProfile() { val responseSnapshot = this.javaClass.getResource("/userDetailResponse.json").readText() val user = Gson().fromJson<User>(responseSnapshot, User::class.java) val response = Respon
@Test
fun getUserProfile() {
val responseSnapshot = this.javaClass.getResource("/userDetailResponse.json").readText()
val user = Gson().fromJson<User>(responseSnapshot, User::class.java)
val response = Response.success(user)
val deferred = CompletableDeferred<Response<User>>(response)
coEvery { userService.getUserDetail() } returns deferred
viewModel.getUserProfile()
assert(viewModel.loadingStatus.value != null)
assert(!UITestUtil.getValue(viewModel.loadingStatus)!!)
assertEquals(false, viewModel.loadingStatus.value!!)
}
当我调试测试用例时,它显示UITestUtil.getValue(viewModel.loadingStatus)!!这是错误的
但奇怪的是,测试用例没有通过,当我打印UITestUtil.getValue(viewModel.loadingStatus)时!!。这是真的
它可能与加载status.postValue(true)
删除后,打印结果为false
但我不知道为什么
object UITestUtil {
/**
* Gets the value of a LiveData safely.
*/
@Throws(InterruptedException::class)
fun <T> getValue(liveData: LiveData<T>): T? {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
liveData.removeObserver(this)
}
}
liveData.observeForever(observer)
latch.await(2, TimeUnit.SECONDS)
return data
}
}
更新3:
fun loadAllTasksFromRepository\u LoadingLogles和DataLoaded()
在里面
同样有效,但我尝试在代码中引入,它也失败了
@ExperimentalCoroutinesApi
@Test
fun getUserProfile4Using() {
// using TestCoroutineScope in kotlinx.coroutines.test
// Pause dispatcher so we can verify initial values
mainCoroutineRule.pauseDispatcher()
val responseSnapshot = this.javaClass.getResource("/userDetailResponse.json").readText()
val user = Gson().fromJson<User>(responseSnapshot, User::class.java)
val response = Response.success(user)
val deferred = CompletableDeferred<Response<User>>(response)
// coEvery { userService.getUserDetail() } returns deferred
viewModel.getUserProfile()
assert(viewModel.loadingStatus.value != null)
// verify { viewModel.processUserDetails(user) }
print(viewModel.loadingStatus.value)
assert(UITestUtil.getValue(viewModel.loadingStatus)!!)
assertEquals(true, viewModel.loadingStatus.value!!)
// Execute pending coroutines actions
mainCoroutineRule.resumeDispatcher()
print(viewModel.loadingStatus.value!!)
assert(!UITestUtil.getValue(viewModel.loadingStatus)!!)
assertEquals(false, viewModel.loadingStatus.value!!)
}
@experimentalRoutinesAPI
@试验
fun getUserProfile4Using(){
//在kotlinx.coroutines.test中使用TestCoroutineScope
//暂停调度程序,以便我们可以验证初始值
mainCoroutineRule.pauseDispatcher()
val responseSnapshot=this.javaClass.getResource(“/userDetailResponse.json”).readText()
val user=Gson().fromJson(responseSnapshot,user::class.java)
val response=response.success(用户)
val deferred=CompletableDeferred(响应)
//coEvery{userService.getUserDetail()}返回延迟的
viewModel.getUserProfile()
断言(viewModel.loadingStatus.value!=null)
//验证{viewModel.processUserDetails(用户)}
打印(viewModel.loadingStatus.value)
断言(UITestUtil.getValue(viewModel.loadingStatus)!!)
assertEquals(true,viewModel.loadingStatus.value!!)
//执行挂起的协同路由操作
mainCoroutineRule.resumeDispatcher()文件
打印(viewModel.loadingStatus.value!!)
断言(!UITestUtil.getValue(viewModel.loadingStatus)!!)
assertEquals(false,viewModel.loadingStatus.value!!)
}
我认为您可能应该使用运行blockingtest来重写您的测试
fun getUserProfile()=运行blockingtest{
val responseSnapshot=this.javaClass.getResource(“/userDetailResponse.json”).readText()
val user=Gson().fromJson(responseSnapshot,user::class.java)
val response=response.success(用户)
val deferred=CompletableDeferred(响应)
coEvery{userService.getUserDetail()}返回延迟的
viewModel.getUserProfile()
断言(viewModel.loadingStatus.value!=null)
断言(!UITestUtil.getValue(viewModel.loadingStatus)!!)
assertEquals(false,viewModel.loadingStatus.value!!)
}
您不必使用那些倒计时锁等。这种方法实际上解决了我在测试协同程序时遇到的所有问题,希望它也能帮助您:)!如果没有,请告诉我。谢谢您的回答。我粘贴了你的代码并进行了测试,但仍然失败。调试时,UITestUtil.getValue(viewModel.loadingStatus)!!仍然返回false。但奇怪的是,它仍然未能通过测试。你需要提供更多的代码-你的callAsync ext函数做什么?更新了它。似乎coEvery正在挂起其余语句:if(user!=null){processUserDetails(user)}loadingStatus.postValue(false)
import com.google.gson.Gson
import kotlinx.coroutines.Deferred
import retrofit2.Response
import retrofit2.Response.success
data class VPlusResult<T : Any?>(
val response: Response<T>? = null,
val exception: Exception? = null
)
inline fun <T : Any> VPlusResult<T>.onSuccess(action: (T?) -> Unit): VPlusResult<T> {
if (response?.isSuccessful == true)
action(response.body())
return this
}
inline fun <T : Any> VPlusResult<T>.onRawSuccess(action: (response: Response<T>) -> Unit): VPlusResult<T> {
if (response?.isSuccessful == true)
action(response)
return this
}
inline fun <T : Any, TR : Any> VPlusResult<T>.map(action: (T?) -> (TR)) =
if (response?.isSuccessful == true) VPlusResult(success(action(response.body())))
else this as VPlusResult<TR>
inline fun <T : Any> VPlusResult<T>.onError(action: (String) -> Unit): VPlusResult<T> {
if (response?.isSuccessful != true) {
response?.errorBody()?.let {
action(it.string())
}
}
return this
}
inline fun <T : Any, reified G : Any> VPlusResult<T>.onErrorJson(action: (G) -> Unit): VPlusResult<T> {
if (response?.isSuccessful != true) {
response?.errorBody()?.let {
action(Gson().fromJson(it.string(), G::class.java))
}
}
return this
}
inline fun <T : Any> VPlusResult<T>.onRawError(action: (Response<T>?) -> Unit): VPlusResult<T> {
if (response?.isSuccessful != true) {
action(response)
}
return this
}
inline fun <T : Any?> VPlusResult<T>.onException(action: (Exception) -> Unit) {
exception?.let { action(it) }
}
inline fun <T : Any> VPlusResult<T>.onSadness(action: (String?) -> Unit): VPlusResult<T> {
onError {
action(it)
}.onException {
action(it.message)
}
return this
}
suspend fun <T : Any> callAsync(block: () -> Deferred<Response<T>>): VPlusResult<T> {
return try {
VPlusResult(block().await())
} catch (e: Exception) {
VPlusResult(exception = e)
}
}
coVerify { userService.getUserDetail() }
coVerify { viewModel.processUserDetails(user) }
@ExperimentalCoroutinesApi
@Test
fun getUserProfile4Using() {
// using TestCoroutineScope in kotlinx.coroutines.test
// Pause dispatcher so we can verify initial values
mainCoroutineRule.pauseDispatcher()
val responseSnapshot = this.javaClass.getResource("/userDetailResponse.json").readText()
val user = Gson().fromJson<User>(responseSnapshot, User::class.java)
val response = Response.success(user)
val deferred = CompletableDeferred<Response<User>>(response)
// coEvery { userService.getUserDetail() } returns deferred
viewModel.getUserProfile()
assert(viewModel.loadingStatus.value != null)
// verify { viewModel.processUserDetails(user) }
print(viewModel.loadingStatus.value)
assert(UITestUtil.getValue(viewModel.loadingStatus)!!)
assertEquals(true, viewModel.loadingStatus.value!!)
// Execute pending coroutines actions
mainCoroutineRule.resumeDispatcher()
print(viewModel.loadingStatus.value!!)
assert(!UITestUtil.getValue(viewModel.loadingStatus)!!)
assertEquals(false, viewModel.loadingStatus.value!!)
}
fun getUserProfile() = runBlockingTest{
val responseSnapshot = this.javaClass.getResource("/userDetailResponse.json").readText()
val user = Gson().fromJson<User>(responseSnapshot, User::class.java)
val response = Response.success(user)
val deferred = CompletableDeferred<Response<User>>(response)
coEvery { userService.getUserDetail() } returns deferred
viewModel.getUserProfile()
assert(viewModel.loadingStatus.value != null)
assert(!UITestUtil.getValue(viewModel.loadingStatus)!!)
assertEquals(false, viewModel.loadingStatus.value!!)
}