无法在ViewModel中测试Livedata

发布于 2025-01-27 01:46:55 字数 3418 浏览 3 评论 0原文

因此,我有这个ViewModel类

@HiltViewModel
class HomeViewModel @Inject constructor(private val starWarsRepository: StarWarsRepository) : ViewModel() {

    /**
     * Two-Way binding variable
     */
    val searchQuery: MutableLiveData<String> = MutableLiveData()

    val characters = searchQuery.switchMap { query ->
        if (query.isNotBlank() && query.isNotEmpty()) {
            characterPager.liveData.cachedIn(viewModelScope)
        } else {
            MutableLiveData()
        }
    }

    fun retrySearch() {
        searchQuery.postValue(searchQuery.value)
    }

    private val characterPager by lazy {
        val searchPagingConfig = PagingConfig(pageSize = 20, maxSize = 100, enablePlaceholders = false)
        Pager(config = searchPagingConfig) {
            CharacterSearchPagingSource(starWarsRepository, searchQuery.value ?: String())
        }
    }
}

,这里是反对测试频道,

@ExperimentalCoroutinesApi
@RunWith(JUnit4::class)
class HomeViewModelTest {

    @get:Rule
    val instantTaskExecutionRule = InstantTaskExecutorRule()

    private lateinit var homeViewModel: HomeViewModel
    private val starWarsAPI = mock(StarWarsAPI::class.java)
    private val testDispatcher = UnconfinedTestDispatcher()

    @Before
    fun setup() {
        Dispatchers.setMain(testDispatcher)
        homeViewModel = HomeViewModel(StarWarsRepositoryImpl(starWarsAPI, testDispatcher))
    }

    @AfterTest
    fun tearDown() {
        Dispatchers.resetMain()
    }

    @Test
    fun `live data should emit success with result when searching for a character`() = runTest {
        val character = CharacterEntity("", "", "", "172", "", listOf(), listOf())
        `when`(starWarsAPI.searchCharacters("a", null)).thenReturn(CharacterSearchResultEntity(listOf(character, character), null, null))

        homeViewModel.searchQuery.value = "a"
        val result = homeViewModel.characters.getOrAwaitValue()

        assertEquals(PagingSource.LoadResult.Page(listOf(), null, null), result)
    }
}

存储库

class StarWarsRepositoryImpl(private val starWarsAPI: StarWarsAPI, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO) : StarWarsRepository {

    override suspend fun searchCharacters(query: String, page: Int?) = withContext(coroutineDispatcher) {
        starWarsAPI.searchCharacters(query, page)
    }
}

这是我使用的是Google样本中的该扩展功能的

fun <T> LiveData<T>.getOrAwaitValue(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()
            [email protected](this)
        }
    }
    this.observeForever(observer)
    try {
        afterObserve.invoke()
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }
    } finally {
        this.removeObserver(observer)
    }
    @Suppress("UNCHECKED_CAST")
    return data as T
}

。问题是我从getorawaitValue livedata值从未设置为

So, I have this ViewModel class

@HiltViewModel
class HomeViewModel @Inject constructor(private val starWarsRepository: StarWarsRepository) : ViewModel() {

    /**
     * Two-Way binding variable
     */
    val searchQuery: MutableLiveData<String> = MutableLiveData()

    val characters = searchQuery.switchMap { query ->
        if (query.isNotBlank() && query.isNotEmpty()) {
            characterPager.liveData.cachedIn(viewModelScope)
        } else {
            MutableLiveData()
        }
    }

    fun retrySearch() {
        searchQuery.postValue(searchQuery.value)
    }

    private val characterPager by lazy {
        val searchPagingConfig = PagingConfig(pageSize = 20, maxSize = 100, enablePlaceholders = false)
        Pager(config = searchPagingConfig) {
            CharacterSearchPagingSource(starWarsRepository, searchQuery.value ?: String())
        }
    }
}

and here it's counter TestingClass

@ExperimentalCoroutinesApi
@RunWith(JUnit4::class)
class HomeViewModelTest {

    @get:Rule
    val instantTaskExecutionRule = InstantTaskExecutorRule()

    private lateinit var homeViewModel: HomeViewModel
    private val starWarsAPI = mock(StarWarsAPI::class.java)
    private val testDispatcher = UnconfinedTestDispatcher()

    @Before
    fun setup() {
        Dispatchers.setMain(testDispatcher)
        homeViewModel = HomeViewModel(StarWarsRepositoryImpl(starWarsAPI, testDispatcher))
    }

    @AfterTest
    fun tearDown() {
        Dispatchers.resetMain()
    }

    @Test
    fun `live data should emit success with result when searching for a character`() = runTest {
        val character = CharacterEntity("", "", "", "172", "", listOf(), listOf())
        `when`(starWarsAPI.searchCharacters("a", null)).thenReturn(CharacterSearchResultEntity(listOf(character, character), null, null))

        homeViewModel.searchQuery.value = "a"
        val result = homeViewModel.characters.getOrAwaitValue()

        assertEquals(PagingSource.LoadResult.Page(listOf(), null, null), result)
    }
}

Here is the repository

class StarWarsRepositoryImpl(private val starWarsAPI: StarWarsAPI, private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO) : StarWarsRepository {

    override suspend fun searchCharacters(query: String, page: Int?) = withContext(coroutineDispatcher) {
        starWarsAPI.searchCharacters(query, page)
    }
}

I'm using this extension function from a google sample.

fun <T> LiveData<T>.getOrAwaitValue(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()
            [email protected](this)
        }
    }
    this.observeForever(observer)
    try {
        afterObserve.invoke()
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }
    } finally {
        this.removeObserver(observer)
    }
    @Suppress("UNCHECKED_CAST")
    return data as T
}

The problem is I'm getting an exception from the getOrAwaitValue that LiveData value was never set.?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文