例如,我有一个改造接口,例如:
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()正常工作。
我应该如何测试?预先感谢。
答案 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个选择:
Mockito.any()
,基本上可以说,无论您的方法将用作参数,您都将返回模拟的行为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)