如何对改装电话进行单元测试?

时间:2018-12-10 14:36:58

标签: android unit-testing mockito retrofit2

例如,我有一个改造接口,例如:

interface SampleService {
    fun getSomething(@body someBody: SomeBody)
}

现在我有一个使用此接口的类,例如:

class UserRequester(val service: SampleService) {
     fun doGetSomething(someValue: String) {
         val response = service.getSomething(SomeBody(someValue))
         // ...
     }
 }

我想测试该类,但不知道如何模拟它。

我正在尝试以下操作:

val mockSampleService = mock()
val userRequester = UserRequester(mockSampleService)
val requestBody = SomeBody(someString))
  when(mockSampleService.getSomething(requestBody)).return(myExpectedValue)
....

我的问题是,由于我在函数内部创建了请求对象,因此从技术上讲,我要传递两个不同的对象,因此无法使模拟when()。thenReturn()正常工作。

我应该如何测试?预先感谢。

3 个答案:

答案 0 :(得分:1)

问题是SomeBody的构造函数存在静态依赖性:

val response = service.getSomething(SomeBody(someValue))

要控制SomeBody的实例,您可以做的是使用“提供者”或“工厂”对象,您可以将其注入构造函数中并在适当的时候调用它:

interface SampleService {
    fun getSomething(someBody: SomeBody)
}

open class SomeBody(val body: String)

open class UserRequester(
    val service: SampleService,
    val someBodyProvider: (String) -> SomeBody
) {

    fun doGetSomething(someValue: String) {
        val response = service.getSomething(someBodyProvider(someValue))
    }
}

并在测试中对其进行模拟:

val someValue = "foo"
val sampleService: SampleService = mock()
val someBody: SomeBody = mock()
val someBodyProvider: (String) -> SomeBody = mock {
    on { invoke(someValue) }.thenReturn(someBody)
}
val userRequester = UserRequester(sampleService, someBodyProvider)

userRequester.doGetSomething("foo")

verify(sampleService).getSomething(someBody)
verify(someBodyProvider).invoke(someValue)

我使用了匿名函数,但您也可以将其设置为interface

答案 1 :(得分:1)

嘲讽问题(UserRequester)

您不能模拟mockSampleService方法,因为您的类正在创建SomeBody对象,并且与您在测试中创建的SomeBody对象不同。

现在您有2个选择:

  1. 在测试中使用Mockito.any(),基本上可以说,无论您的方法将用作参数,您都将返回模拟的行为
  2. 使用给定someString的工厂将为您返回SomeObject,如下所示:

// the factory
class SomeObjectFactory{

    fun createSomeObject(someString: String): SomeObject {
        return SomeObject(someString)
    }

}

//the class
class UserRequester(
val service: SampleService, val factory: SomeObjectFactory
) {
     fun doGetSomething(someValue: String) {
         val response = service.getSomething(factory.createSomeObject(someValue))
         // ...
     }
 }

//the test
class MyTest{

    @Test
    fun myTestMethod(){
        val mockSampleService = mock()
        val factory = mock()
        val someBody = mock()
        val userRequester = UserRequester(mockSampleService, factory)
        `when`(factory.createSomeObject(someString)).thenReturn(someBody)

  `when`(mockSampleService.getSomething(someBody)).thenReturn(myExpectedValue)
    //rest of the code
    }

}

第二种方法是最干净的。

测试改造调用(SampleService)

我不会unit test进行改造电话。

在处理框架,api,数据库时,共享首选项总是比integration tests更好,而不是unit tests

通过这种方式,您实际上正在测试您的代码是否与外部环境兼容。

我建议您使用MockWebServer(这是Square的库,它是开发OkHttp和Retrofit的同一家公司)来测试Retrofit调用的。

read可能也有帮助。

答案 2 :(得分:1)

SomeBody可能是纯值对象,因为翻新请求适用于值对象。如果为equals类定义SomeBody方法,则eq匹配器将起作用,并且可以使用mockito-kotlin进行写:

whenever(mockService.getSomething(eq(SomeBody(someString)))).thenReturn(stubbedResult)

实际上,您可以省略eq匹配器,Mockito将使用equals方法进行匹配。

如果SomeBody是Kotlin data class,则equals方法是通过比较字段自动定义的。

如果由于某些原因您不想依靠equals,则可以使用在嘲讽-kotlin中定义的argThat匹配器:

whenever(mockService.getSomething(argThat { theField == someValue })).thenReturn(stubbedResult)