Android 单元测试 kotlin:LiveData 值从未设置

Jon*_*han 2 junit android unit-testing kotlin android-studio

我在单元测试时收到此错误:“LiveData 值从未设置。”

我正在尝试测试我的视图模型的 getStudents() 方法,如下所示:

学生视图模型:

@HiltViewModel
class StudentViewModel @Inject constructor(
    private val getStudentsUseCase: GetStudentsUseCase
) : ViewModel() {
    private val _studentLD = MutableLiveData<List<Student>>()
    val studentLD: LiveData<List<Student>> = _studentLD

    private val _errorLD = MutableLiveData<String>()
    val errorLD: LiveData<String> = _errorLD

    fun getStudents() {
        viewModelScope.launch {
            var result = getStudentsUseCase.execute()
            result.onSuccess {
                _studentLD.value = it
            }.onFailure {
                _errorLD.value = it.message.toString()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

学生视图模型测试:

@OptIn(ExperimentalCoroutinesApi::class)
class StudentViewModelTest {

    private lateinit var viewModel: StudentViewModel

    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()

    @get:Rule
    var mainCoroutineRule = MainCoroutineRule()

    @Mock
    private lateinit var getStudentsUseCase: GetStudentsUseCase

    private var studentList = listOf(
        Student("Max", "122d"),
        Student("Steven", "012s")
    )

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        viewModel = StudentViewModel(getStudentsUseCase)
    }

    @Test
    fun getStudentsSuccess() = runTest {
        Mockito.`when`(getStudentsUseCase.execute()).thenReturn(Result.success(studentList))

        viewModel.getStudents()
        val value = viewModel.studentLD.getOrAwaitValueTest()
        assertEquals(value, studentList)
    }
}
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?我还有一个 LiveDataTest 类,其中定义了 getOrAwaitValueTest() 方法。先感谢您

编辑:另外, getOrAwaitValueTest() 方法在这里定义如下:

@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValueTest(
    time: Long = 2,
    timeUnit: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): T {
    var data: T? = null
    val latch = CountDownLatch(1)
    val observer = object : Observer<T> {
        override fun onChanged(o: T?) {
            data = o
            latch.countDown()
            this@getOrAwaitValueTest.removeObserver(this)
        }
    }
    this.observeForever(observer)

    try {
        afterObserve.invoke()

        // Don't wait indefinitely if the LiveData is not set.
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }

    } finally {
        this.removeObserver(observer)
    }

    @Suppress("UNCHECKED_CAST")
    return data as T
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*han 6

我找到了一个解决方案:为了使其正常工作,您需要添加 advanceUntilIdle() 方法。

立即执行所有挂起的任务并将虚拟时钟时间提前到最后的延迟。如果由于虚拟时间提前而安排新任务,它们将在 advanceUntilIdle 返回之前执行。

@Test
fun getStudentsSuccess() = runTest {
        Mockito.`when`(getStudentsUseCase.execute()).thenReturn(Result.success(studentList))

        viewModel.getStudents()
        advanceUntilIdle()
        val value = viewModel.studentLD.getOrAwaitValueTest()

        assertEquals(value, studentList)
}
Run Code Online (Sandbox Code Playgroud)