junit 使用mockk在Kotlin中测试异步函数时的IndexOutOfBoundsExpection

bvpmtnay  于 2023-10-20  发布在  Kotlin
关注(0)|答案(1)|浏览(165)

当测试一个函数时,我遇到了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))
}
7y4bm7vi

7y4bm7vi1#

viewModel.getUsersInRangeResource.Success(response)发送到MutableLiveData,然后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))
}

相关问题