在Ok188bet金宝搏官网cupid,我们经常使用Mockito.用于在JUnit测试中使用依赖项的模拟库的库。这允许我们轻松模拟某些方法的返回值,或者验证在我们的依赖项之一上调用方法,但它可以提供并发症作为所测试的组件之间的集成,并且其依赖关系变得更加复杂。

在这篇文章中,我们将通过嘲弄图书馆找到一个限制,并讨论如何通过使用我们自己的假实施来超越这一限制。让我们首先设置问题阶段。

设置舞台

在我们讨论问题之前,让我们确保我们理解正在测试的组件。我们将在查看负责进行网络请求并显示一些结果的ViewModel。

下面是ViewModel的片段,但如果您想看到相关的类,您可以找到它们这个小麦。请注意,在此视图中,我们一旦创建,我们就会要求在内部的配置文件信息在里面方法:

class profileviewmodel(userid:string,存储库:profilerepository,backgroundscheduler:scheduler = schedulers.io(),mathercheduler:scheduler = androidschedul188博金宝电子体育频道ers.mainthread()):ViewModel()):ViewModel(){private val _state = mutableLiveData ()Val状态:LiveData = _state init {_state.value = profileviewstate.loading()存储库.cetchprofile(UserID).subscribeon(backgroundscheduler).observeon(mainscheduler).subscribe({user  - > _state.value = profileviewstate.success(User)},{ERROR  - > _STATE.VALUE = PRINFICEVIEWSTATE.ERROR(ERROR)})}}}

一旦创建了我们的视图,我们就会向我们的LiveData发出加载状态。然后,如果呼叫成功或失败,我们将请求配置文件,并发布新的ProfileViewState。

这是我们正在测试的组件所需的一切。接下来我们可以测试它。

测试ViewModel.

我们将从一个正案例测试开始,以确保当我们请求用户时,将发出加载状态,然后是数据状态。让我们看看测试看起来像什么:

class profileviewmodeltest {@test fun loadprofile(){val testuser = user(userid =“123”)val mockrepository = mockito.mock。just(testUser)) val viewModel = ProfileViewModel( userId = "123", repository = mockRepository, backgroundScheduler = Schedulers.trampoline(), mainScheduler = Schedulers.trampoline() ) val observedStates = viewModel.state.testObserver().observedValues assertThat(observedStates.size).isEqualTo(2) val firstState = observedStates[0] val secondState = observedStates[1] assertThat(firstState.loading).isTrue() assertThat(secondState.data).isEqualTo(testUser) } }

如果您想看到实施.testobserver()你可以找到它这个小麦

测试失败

令我们惊讶的是,这个测试将失败!我们正在嘲笑成功的数据请求,所以我们应该期待我们的观察到的有两个条目:一个用于加载状态,一个用于成功的数据状态。运行测试后,第一个断言失败。我们的测试说观察到是一个,值是数据状态。

换句话说,我们的测试无法验证发生加载状态。

发生了什么?

让我们考虑我们的单位测试中的不同之处而不是实际代码。在我们的单位测试中,我们通过了scheduler.trampoline()来自rxjava,有助于使网络请求运行好像它是顺序的。就此测试而言,在创建ViewModel后,它就好像网络请求立即成功。

然后,在创建我们的视图后,我们将测试观察者应用于ViewModel.State.LiveData已经处于加载数据状态。这意味着加载状态发生在太远的时候 - 我们无法在创建之前观察一个Liveata,因此我们无法验证发生的加载状态。

这种并发症是由我们的嘲弄库引起的,它告诉我们的模拟存储库立即返回信息。相反,我们可以创建我们自己的虚假实现我们完全控制的ProfilerePository,并可以控制数据的排放,以确保我们的单元测试捕获加载状态。

创造假.

要创建虚假实现,我们首先创建实现我们接口的新类。请记住,我们不希望我们的假期立即返回数据,因为这会导致同样的问题。相反,由于我们正在使用rxjava,我们可以以一种使用的方式实现我们的假行为赞许我们可以控制的幕后。

class fakerepository:profilerepository {private valusersubject:行为,行为,行为resubject.create()覆盖乐趣fetchprofile(userid:string):single  {return userubject.hide()。firstorerror()}。firstorerror()。

如果您使用的是Coroutines,则在此处使用此处可能会更改,但该概念保持不变:我们不想从中返回fetchprofile()立即提供信息。我们希望确保在发出数据时精确地控制我们的假实施控制。

控制数据排放量

由于我们的假实施是使用的行为赞许作为底层数据源,我们可以在我们喜欢的情况下创建自己的公共方法来发射它:

class fakerepository:profilerepository {// ...有趣的发出者(user:user){this.ustersubject.onnext(user)}}

更新测试以验证加载状态

现在我们有一个系统到位,我们在从我们的存储库发出数据时我们有很大的粗粒控制,我们可以利用这一点来准确地测试我们的加载状态。我们将遵循此配方:

  1. 创建我们的假存储库和ViewModel组件
  2. 由于我们的假期不立即发出数据,我们将能够验证我们处于加载状态。
  3. 我们可以控制伪造实现以发出数据。
  4. 最后,我们可以验证我们的ViewModel现在处于加载的数据状态。
class profileviewmodeltest {@test fun loadprofile(){val testuser = user(userid =“123”)//使用假文件而不是mockito val fakerepository = fakerepository()//创建我们的视图显示valipemodel = profileviewmodel(userid =“123”,存储库= fakerepository,backgroundscheduler = schedulers.trampoline(),mainscheduler = schedulers.trampoline())valisobserver = ViewModel.State.Testobserver()val观察静验= testobserver.observedValues //验证我们只有加载状态assertthat(观察到的静态istthat).isequalto(1)val firststate =观察到的静脉[0] assertthat(firststate.load).istrue()//发出数据,验证我们移动到一个//加载的数据状态fakerepository.emituser(testuser)assertthat(观察到的states.size).isequalto(2)val secondstate =观察器[1] assertthat(secondstate.data).isequalto(testuser)}}

搭档

模拟库提供了一种快速解决方案,用于创建在我们的Android单元测试中使用的依赖项,但是以牺牲对控制这些依赖性的行为的限制来实现。188博金宝电子体育频道通过利用我们代码库中存在的接口和我们自己的假实现,我们可以完全控制依赖项,我们可以使用它来控制反应流的数据的排放,以彻底的单元测试我们的组件。

我希望你找到这个有用的!如果您有其他使用假的例子与模拟,请告诉我推特

有兴趣为Okcupid工作吗?188bet金宝搏官网我们正在招聘!!