简体   繁体   English

使用 RxJava 时对 LiveData 进行本地单元测试

[英]Local unit test for LiveData while using RxJava

Full source code is available at: https://github.com/Ali-Rezaei/StarWarsSearch-RxPaging完整的源代码在: https://github.com/Ali-Rezaei/StarWarsSearch-RxPaging

I am trying to test my DetailViewModel.我正在尝试测试我的 DetailViewModel。 My expectation is Species and Films not be empty lists as I have for instance: when(service.getSpecie(anyString())).thenReturn(Single.just(specie)) .我的期望是 Species 和 Films 不是空列表,例如: when(service.getSpecie(anyString())).thenReturn(Single.just(specie)) Here is my test:这是我的测试:

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"))
        }
    }
}
}

Here is my ViewModel:这是我的视图模型:

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)
        }))

And here is my BaseViewModel:这是我的 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()
    }
}

And here is DetailWrapper class:这是DetailWrapper class:

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

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

Why films and species lists are empty in my local unit test?为什么电影和物种列表在我的本地单元测试中是空的?

As you see I pass two emptyLists to Character object.如您所见,我将两个空列表传递给字符 object。 That is the source of problem since for instance I have following in DetailViewModel:这是问题的根源,因为例如我在 DetailViewModel 中有以下内容:

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

FilmUrls is one of those emptyLists. FilmUrls是这些空列表之一。 If I change Character by passing not emptyList, it is working as expected:如果我通过传递 not emptyList 来更改 Character,它会按预期工作:

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

I also need to move ViewModel initialization to the method body, such as:我还需要将 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())
                }
            }
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM