用于测试 rxjava 的书面单元测试,但不确定我的单元测试是不是正在测试所有内容

Posted

技术标签:

【中文标题】用于测试 rxjava 的书面单元测试,但不确定我的单元测试是不是正在测试所有内容【英文标题】:Written unit test for testing rxjava, but not sure if my unit test is testing everything用于测试 rxjava 的书面单元测试,但不确定我的单元测试是否正在测试所有内容 【发布时间】:2019-05-25 12:09:37 【问题描述】:
android Studio 3.4

我正在测试以下方法。基本上,这个测试所做的是发出一个请求,这将返回一个LoginResponseEntity,该LoginResponseEntity将被映射并返回一个Single<LoginResponse>

 override fun loginUserPost(username: String, password: String, uniqueIdentifier: String, deviceToken: String, apiToken: String) : Single<LoginResponse> 
            val loginRequestEntity = LoginRequestEntity(username, password, uniqueIdentifier, deviceToken)
            return loginAPIService.loginUserPost(loginRequestEntity, apiToken)
                .map 
                    loginResponseDomainMapper.map(it)
                
    

我写的测试用例有效,但我认为这并没有完全测试这种方法。

     @Test
     fun `should return LoginResponse`() 
        val loginRequestEntity = LoginRequestEntity("username", "password", "uniqueidentifier", "devicetoken")
        val loginResponse = LoginResponse("token", createUser(), emptyList(), emptyList())
        val loginResponseEntity = LoginResponseEntity("token", createUserEntity(), emptyList(), emptyList())

        whenever(loginAPIService.loginUserPost(loginRequestEntity, "apitoken")).thenReturn(Single.just(loginResponseEntity))

        loginServiceImp.loginUserPost("username", "password", "uniqueidentifier", "devicetoken", "apitoken")
            .test()
            .assertValue(loginResponse)

        verify(loginAPIService).loginUserPost(loginRequestEntity, "apitoken")
    

        private fun createUser() =
            User(
                "id",
                "email",
                "firstname",
                "lastname",
                "phone",
                "address",
                "dob",
                "customer",
                listOf("enterpriseids"),
                listOf("vendorids"))

        private fun createUserEntity() =
            UserEntity(
                "id",
                "email",
                "firstname",
                "lastname",
                "phone",
                "address",
                "dob",
                "customer",
                listOf("enterpriseids"),
                listOf("vendorids"))
    

我还能做些什么来测试这个方法。我应该测试这个方法的.maploginResponseDomainMapper.map(it) 部分吗?

【问题讨论】:

通常你首先测试的是函数的结果。您可以使用 TestScheduler 来做到这一点 除非您使用某种代码覆盖率工具,否则您永远不会知道您的代码是否经过全面测试,您可以尝试jacoco 甚至idea 包含一个 你只是在测试快乐的道路。如果loginAPIService.loginUserPost() 方法失败了怎么办? 【参考方案1】:

这是一个非常小的方法,不包含很多要测试的东西。两个外部依赖项(loginAPIServiceloginResponseDomainMapper)减少了更多需要测试的东西。

所以,

1) loginResponseDomainMapper 不是测试方法的一部分,也应该被模拟。

2) 你必须明白,这里应该测试什么。

首先:检查LoginRequestEntity 是否正确构造并传递给loginUserPost 方法。这是通过verify(loginAPIService).loginUserPost(loginRequestEntity, "apitoken") 调用完成的。此外,您可以使用ArgumentCaptor。 第二:loginUserPost 方法的输出已正确传递给loginResponseDomainMapper.map 方法。这可以像以前一样通过额外的verify 调用来完成。 第三:map方法的输出正确返回。这是通过assertValue 调用完成的。

因此,您只是在尝试验证数据流是否正确,并且在执行过程中外星人或类似的东西没有修改任何内容。

3) 负面测试。也没有多少事情会出错。如果loginUserPost没有@NotNull注解,你最好处理null这个函数的结果。 另外,如果请求不正确怎么办?密码错误,或apitoken 已过期?我相信这不会导致悲惨的后果,但你应该考虑一下。

【讨论】:

【参考方案2】:

您可以为不成功的场景添加测试:

1)如果loginUserPost失败,可以添加:

whenever(loginAPIService.loginUserPost(any(), any())).thenReturn(Single.error(Exception()))
...
   .test()
   .assertError(...)

2)如果loginResponseDomainMapper失败,可以添加:

whenever(loginResponseDomainMapper.map(any())).thenThrow(Exception())
...
       .test()
       .assertError(...)

这样您就可以测试不成功/成功的映射和函数输出,而无需详细说明。 loginResponseDomainMapper 行为应在其自身的单元测试范围内进行测试。

【讨论】:

以上是关于用于测试 rxjava 的书面单元测试,但不确定我的单元测试是不是正在测试所有内容的主要内容,如果未能解决你的问题,请参考以下文章

对有延迟的 Rxjava 可观察对象进行单元测试

单元测试 RxJava 超时未订阅

RxJava 和改造的单元测试

在RxJava的doOnSuccess运算符中调用了单元测试验证方法

使用 Completable.timer 对 RxJava 进行单元测试

如何调整我的视图模型以实现依赖注入 swiftui(用于以后的单元测试)