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

问题描述 投票:0回答:1

测试某个功能时,我遇到了

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))
}
kotlin asynchronous junit kotlin-coroutines mockk
1个回答
0
投票

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))
}
© www.soinside.com 2019 - 2024. All rights reserved.