【问题标题】:Local unit test for LiveData while using RxJava使用 RxJava 时对 LiveData 进行本地单元测试
【发布时间】:2021-06-16 15:15:37
【问题描述】:

完整的源代码在:https://github.com/AliRezaeiii/StarWarsSearch-RxPaging

我正在尝试测试我的 DetailViewModel。我的期望是 Species 和 Films 不是空列表,例如:when(service.getSpecie(anyString())).thenReturn(Single.just(specie))。这是我的测试:

class DetailViewModelTest {

@get:Rule
var rule: TestRule = InstantTaskExecutorRule()

@Mock
private lateinit var service: StarWarsService

private lateinit var specie: Specie
private lateinit var planet: Planet
private lateinit var film: Film

private lateinit var viewModel: DetailViewModel

@Before
fun setUp() {
    initMocks(this)

    // Make the sure that all schedulers are immediate.
    val schedulerProvider = ImmediateSchedulerProvider()

    val detailRepository = DetailRepository(service)
    val character = Character(
        "Ali", "127", "1385", emptyList(), emptyList()
    )

    viewModel = DetailViewModel(
        schedulerProvider, character, GetSpecieUseCase(detailRepository),
        GetPlanetUseCase(detailRepository), GetFilmUseCase(detailRepository)
    )

    specie = Specie("Ali", "Persian", "Iran")
    planet = Planet("")
    film = Film("")
}

@Test
fun givenServerResponse200_whenFetch_shouldReturnSuccess() {
    `when`(service.getSpecie(anyString())).thenReturn(Single.just(specie))
    `when`(service.getPlanet(anyString())).thenReturn(Single.just(planet))
    `when`(service.getFilm(anyString())).thenReturn(Single.just(film))

    viewModel.liveData.value.let {
        assertThat(it, `is`(notNullValue()))
        if (it is Resource.Success) {
            it.data?.let { data ->
                assertTrue(data.films.isEmpty())
                assertTrue(data.species.isEmpty())
            }
        }
    }
}

@Test
fun givenServerResponseError_whenFetch_specie_shouldReturnError() {
    `when`(service.getSpecie(anyString())).thenReturn(Single.error(Exception("error")))
    `when`(service.getPlanet(anyString())).thenReturn(Single.just(planet))
    `when`(service.getFilm(anyString())).thenReturn(Single.just(film))

    viewModel.liveData.value.let {
        assertThat(it, `is`(notNullValue()))
        if (it is Resource.Error) {
            assertThat(it.message, `is`(notNullValue()))
            assertThat(it.message, `is`("error"))
        }
    }
}
}

这是我的视图模型:

class DetailViewModel @Inject constructor(
        schedulerProvider: BaseSchedulerProvider,
        character: Character,
        getSpecieUseCase: GetSpecieUseCase,
        getPlanetUseCase: GetPlanetUseCase,
        getFilmUseCase: GetFilmUseCase,
) : BaseViewModel<DetailWrapper>(schedulerProvider,
        Single.zip(Flowable.fromIterable(character.specieUrls)
                .flatMapSingle { specieUrl -> getSpecieUseCase(specieUrl) }
                .flatMapSingle { specie ->
                    getPlanetUseCase(specie.homeWorld).map { planet ->
                        SpecieWrapper(specie.name, specie.language, planet.population)
                    }
                }.toList(),
                Flowable.fromIterable(character.filmUrls)
                        .flatMapSingle { filmUrl -> getFilmUseCase(filmUrl) }
                        .toList(), { species, films ->
            DetailWrapper(species, films)
        }))

这是我的 BaseViewModel :

open class BaseViewModel<T>(
    private val schedulerProvider: BaseSchedulerProvider,
    private val singleRequest: Single<T>
) : ViewModel() {

    private val compositeDisposable = CompositeDisposable()

    private val _liveData = MutableLiveData<Resource<T>>()
    val liveData: LiveData<Resource<T>>
        get() = _liveData

    init {
        sendRequest()
    }

    fun sendRequest() {
        _liveData.value = Resource.Loading
        wrapEspressoIdlingResourceSingle { singleRequest }
            .subscribeOn(schedulerProvider.io())
            .observeOn(schedulerProvider.ui()).subscribe({
                _liveData.postValue(Resource.Success(it))
            }) {
                _liveData.postValue(Resource.Error(it.localizedMessage))
                Timber.e(it)
            }.also { compositeDisposable.add(it) }
    }

    override fun onCleared() {
        super.onCleared()
        compositeDisposable.clear()
    }
}

这里是 DetailWrapper 类:

class DetailWrapper(
    val species: List<SpecieWrapper>,
    val films: List<Film>,
)

class SpecieWrapper(
    val name: String,
    val language: String,
    val population: String,
)

为什么电影和物种列表在我的本地单元测试中是空的?

【问题讨论】:

    标签: android unit-testing rx-java rx-java2


    【解决方案1】:

    如您所见,我将两个空列表传递给 Character 对象。这是问题的根源,因为例如我在 DetailViewModel 中有以下内容:

    Flowable.fromIterable(character.filmUrls)
                            .flatMapSingle { filmUrl -> getFilmUseCase(filmUrl) }
                            .toList()
    

    FilmUrls 是这些空列表之一。如果我通过传递 not emptyList 来更改 Character,它会按预期工作:

    character = Character("Ali", "127", "1385",
                    listOf("url1", "url2"), listOf("url1", "url2"))
    

    我还需要把ViewModel初始化移到方法体中,比如:

        @Test
        fun givenServerResponse200_whenFetch_shouldReturnSuccess() {
            `when`(repository.getSpecie(anyString())).thenReturn(Single.just(specie))
            `when`(repository.getPlanet(anyString())).thenReturn(Single.just(planet))
            `when`(repository.getFilm(anyString())).thenReturn(Single.just(film))
    
            viewModel = DetailViewModel(schedulerProvider, character, GetSpecieUseCase(repository),
                    GetPlanetUseCase(repository), GetFilmUseCase(repository))
    
            viewModel.liveData.value.let {
                assertThat(it, `is`(notNullValue()))
                if (it is Resource.Success) {
                    it.data?.let { data ->
                        assertTrue(data.films.isNotEmpty())
                        assertTrue(data.species.isNotEmpty())
                    }
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2020-03-07
      • 1970-01-01
      • 2020-05-07
      • 1970-01-01
      • 2017-09-19
      • 1970-01-01
      • 2021-08-09
      • 1970-01-01
      • 2017-02-05
      相关资源
      最近更新 更多