测试某个功能时,我遇到了
IndexOutOfBoundsException
的问题。通常,usersInRangeViewStates
的尺寸应为 2,第一个条目为 Resource.Loading
,第二个条目为 Resource.Success
。因为usersInRange(...)
是异步函数,必须等待,所以我添加了runTest{ ... }
和advanceUntilIdle()
。我实际上认为使用 Asserts
进行的测试会等待异步函数完成,但这只是有时会发生。有人看到这个错误吗?或者你能给我建议更好的解决方案吗?
代码:
val usersInRange: MutableLiveData<Resource<GETUsers>> = MutableLiveData()
fun getUsersInRange(myUserGeoPoint: GeoPoint, searchRadiusInMeter: Int) = viewModelScope.launch {
usersInRange.postValue(Resource.Loading())
val response = usersInRange(myUserGeoPoint, searchRadiusInMeter)
usersInRange.postValue(Resource.Success(response))
}
private suspend fun usersInRange(myUserGeoPoint: GeoPoint, searchRadiusInMeter: Int)
= withContext(Dispatchers.IO) {
val usersInRangeBuffer = GETUsers()
if(users.value is Resource.Success) {
for(user in (users.value as Resource.Success<GETUsers>).data.users) {
if(user.value.id == myUserID) continue
if(user.value.isInRange(myUserGeoPoint, searchRadiusInMeter)) {
usersInRangeBuffer.users[user.key] = user.value
}
}
}
usersInRangeBuffer
}
测试:
private lateinit var usersInRangeViewStates: MutableList<Resource<GETUsers>>
@Before
fun setUp() {
usersInRangeViewStates = mutableListOf()
viewModel.usersInRange.observeForever {
usersInRangeViewStates.add(it)
}
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `getUsersInRange() called should return 0 Users from 2 tested Users (plus myUser)`() = runTest {
val myUserGeoPoint = GeoPoint(1.0, 1.0)
val searchRadiusInMeter = 1000
val user01ID = "exampleUser01ID"
val user02ID = "exampleUser02ID"
val user01Location = GETLocation(0.0, 0.0)
val user02Location = GETLocation(0.0, 0.0)
val myUser = GETUser()
val user01 = GETUser(location = user01Location)
val user02 = GETUser(location = user02Location)
val mapUsers = mutableMapOf(myUserID to myUser, user01ID to user01, user02ID to user02)
val users = GETUsers(mapUsers)
val getUsers = Resource.Success(users)
viewModel.users.value = getUsers
viewModel.getUsersInRange(myUserGeoPoint, searchRadiusInMeter)
advanceUntilIdle()
Assert.assertTrue(usersInRangeViewStates[0] is Resource.Loading)
Assert.assertEquals(0, (usersInRangeViewStates[1] as Resource.Success).data.users.size)
Assert.assertFalse((usersInRangeViewStates[1] as Resource.Success).data.users.containsKey(user01ID))
Assert.assertFalse((usersInRangeViewStates[1] as Resource.Success).data.users.containsKey(user02ID))
Assert.assertFalse((usersInRangeViewStates[1] as Resource.Success).data.users.containsKey(myUserID))
}
viewModel.getUsersInRange
将 Resource.Success(response)
发送到 MutableLiveData
,然后要求主线程运行 usersInRangeViewStates.add(it)
。
advanceUntilIdle
将等待“运行观察者”任务被安排。但由于观察是在协程之外完成的,因此它可能不会等待任务实际完成运行。
我认为你可以将测试作为在主线程上运行的另一件事来进行。
withContext(Dispatchers.Main) {
Assert.assertTrue(usersInRangeViewStates[0] is Resource.Loading)
Assert.assertEquals(0, (usersInRangeViewStates[1] as Resource.Success).data.users.size)
Assert.assertFalse((usersInRangeViewStates[1] as Resource.Success).data.users.containsKey(user01ID))
Assert.assertFalse((usersInRangeViewStates[1] as Resource.Success).data.users.containsKey(user02ID))
Assert.assertFalse((usersInRangeViewStates[1] as Resource.Success).data.users.containsKey(myUserID))
}