Java 模拟函数来改变它的行为
Java mock function to change its behavior
我正在为 class 编写单元测试,如下所示:
public class AClass{
private List<String> testStringList;
public void upperMethod(){
testStringList = new ArrayList<String>();
// add some thing to the string here
List<String> newStringList = lowerMethod(testStringList)
// .......
}
public List<String> lowerMethod(testStringList){
//modify the test string list, like reverse it
return modifiedString;
}
}
我想在单元测试 upperMethod
时更改 lowerMethod's
行为。
例如,在 testStringList
后附加一个新项目,而不是反转它。
阅读 mockito doc 后,我产生了一些这样的想法:
public class testClass{
AClass aOriginal;
AClass aSpy;
@Before
public void setUp(){
aOriginal = new AClass();
aSpy = spy(aOriginal);
when(aSpy.lowerMethod(any())).thenAnswer(new Answer() {
public List<String> answer(InvocationOnMock invocation){
Object[] objects = invocation.getArguments();
//Get String list from it somehow, and append a new item, return it
}
});
}
@Test
//test method later with new lowerMethod behavior
}
我想我的主要问题是如何让这个想法发挥作用?最好的方法是什么?
我在这里阅读了多个文档和问题,我的不同之处在于
- 如何通过调用传递字符串列表,即如何为该对象列表提取字符串列表?
- 在
Mockito.when
中,当我通过any()
调用时,答案方法中的列表追加函数总是return一个java.lang.NullPointerException
。
当您处理间谍时,您应该使用 do().when()
语法。这将阻止测试调用实际实现。
也尝试使用更专业的 Mockito.any(List.class)
而不是通用的 Mockito.any()
:
@Before
public void setUp(){
aOriginal = new AClass();
aSpy = spy(aOriginal);
doAnswer(new Answer() {
public List<String> answer(InvocationOnMock invocation){
Object[] objects = invocation.getArguments();
//Get String list from it somehow, and append a new item, return it
})
.when(aSpy).lowerMethod(any(List.class)));
});
}
另一种方法是扩展 Aclass 并使用您想要的任何功能实现 lowerMethod:
public class testClass {
AClassForTest aClass;
@Before
public void setup() {
aClass = new AClassForTest();
}
private class AClassForTest extends AClass{
@Override
public List<String> lowerMethod(List<String> testStringList) {
//append a new item, do whatever you want
return someList;
}
}
}
澄清一下:您应该而不是 "spy" 或"mock" class 正在测试中。 您 "spy" 或 "mock" 它的 依赖项 即:其他 class 提供 业务逻辑 你的测试 class 合作。 监视 睾丸的需要 class 通常指向 设计问题。
如果此模拟方法的 return 值 取决于模拟方法的参数,则只需要 mock.*Answer()
形式。这通常不是你想要的。
通常您想要return一个非常具体的配置return值用于当前测试用例(方法)。因此,您不应该首先在 setup 方法中配置模拟,尤其是如果这意味着使用 mock.*Anser()
.
RestClientService.java的方法-这是核心class,主要与外部HTTP RESTClient通信。因此,This class 在这里被模拟(JUnit 测试)。
public <T> T execute(HttpUriRequest httpUriRequest, ResponseHandler<? extends T> responseHandler) throws IOException {
return this.getClient().execute(httpUriRequest, responseHandler);
}
因此,在服务级别 class,在服务方法中使用不同的处理程序多次调用上述方法....
因此,根据调用方法定义处理程序行为 ...
Mockito.when(RestClientService.execute(Mockito.anyObject(), Mockito.any(ResponseHandler.class))).thenAnswer(answer);
使用答案定义行为 -
private Answer<Object> answer = (InvocationOnMock invocationOnMock) -> {
SearchAccountResponseHandler searchAccountResponseHandler = new SearchAccountResponseHandler();
if (invocationOnMock.getArgumentAt(1, ABCResponseHandler.class) instanceof ABCResponseHandler) {
return new ABCResponseHandler().handleEntity("response as expected for this handler");
}
if (invocationOnMock.getArgumentAt(1, XYZResponseHandler.class) instanceof XYZResponseHandler) {
return new XYZResponseHandler().handleEntity("response as expected for this handler");
}
return new HttpOkResponse(HttpStatus.SC_OK, "default response");
};
最后,最后,这个解决方案是针对我的测试场景的。
我正在为 class 编写单元测试,如下所示:
public class AClass{
private List<String> testStringList;
public void upperMethod(){
testStringList = new ArrayList<String>();
// add some thing to the string here
List<String> newStringList = lowerMethod(testStringList)
// .......
}
public List<String> lowerMethod(testStringList){
//modify the test string list, like reverse it
return modifiedString;
}
}
我想在单元测试 upperMethod
时更改 lowerMethod's
行为。
例如,在 testStringList
后附加一个新项目,而不是反转它。
阅读 mockito doc 后,我产生了一些这样的想法:
public class testClass{
AClass aOriginal;
AClass aSpy;
@Before
public void setUp(){
aOriginal = new AClass();
aSpy = spy(aOriginal);
when(aSpy.lowerMethod(any())).thenAnswer(new Answer() {
public List<String> answer(InvocationOnMock invocation){
Object[] objects = invocation.getArguments();
//Get String list from it somehow, and append a new item, return it
}
});
}
@Test
//test method later with new lowerMethod behavior
}
我想我的主要问题是如何让这个想法发挥作用?最好的方法是什么?
我在这里阅读了多个文档和问题,我的不同之处在于
- 如何通过调用传递字符串列表,即如何为该对象列表提取字符串列表?
- 在
Mockito.when
中,当我通过any()
调用时,答案方法中的列表追加函数总是return一个java.lang.NullPointerException
。
当您处理间谍时,您应该使用 do().when()
语法。这将阻止测试调用实际实现。
也尝试使用更专业的 Mockito.any(List.class)
而不是通用的 Mockito.any()
:
@Before
public void setUp(){
aOriginal = new AClass();
aSpy = spy(aOriginal);
doAnswer(new Answer() {
public List<String> answer(InvocationOnMock invocation){
Object[] objects = invocation.getArguments();
//Get String list from it somehow, and append a new item, return it
})
.when(aSpy).lowerMethod(any(List.class)));
});
}
另一种方法是扩展 Aclass 并使用您想要的任何功能实现 lowerMethod:
public class testClass {
AClassForTest aClass;
@Before
public void setup() {
aClass = new AClassForTest();
}
private class AClassForTest extends AClass{
@Override
public List<String> lowerMethod(List<String> testStringList) {
//append a new item, do whatever you want
return someList;
}
}
}
澄清一下:您应该而不是 "spy" 或"mock" class 正在测试中。 您 "spy" 或 "mock" 它的 依赖项 即:其他 class 提供 业务逻辑 你的测试 class 合作。 监视 睾丸的需要 class 通常指向 设计问题。
如果此模拟方法的 return 值 取决于模拟方法的参数,则只需要 mock.*Answer()
形式。这通常不是你想要的。
通常您想要return一个非常具体的配置return值用于当前测试用例(方法)。因此,您不应该首先在 setup 方法中配置模拟,尤其是如果这意味着使用 mock.*Anser()
.
RestClientService.java的方法-这是核心class,主要与外部HTTP RESTClient通信。因此,This class 在这里被模拟(JUnit 测试)。
public <T> T execute(HttpUriRequest httpUriRequest, ResponseHandler<? extends T> responseHandler) throws IOException {
return this.getClient().execute(httpUriRequest, responseHandler);
}
因此,在服务级别 class,在服务方法中使用不同的处理程序多次调用上述方法.... 因此,根据调用方法定义处理程序行为 ...
Mockito.when(RestClientService.execute(Mockito.anyObject(), Mockito.any(ResponseHandler.class))).thenAnswer(answer);
使用答案定义行为 -
private Answer<Object> answer = (InvocationOnMock invocationOnMock) -> {
SearchAccountResponseHandler searchAccountResponseHandler = new SearchAccountResponseHandler();
if (invocationOnMock.getArgumentAt(1, ABCResponseHandler.class) instanceof ABCResponseHandler) {
return new ABCResponseHandler().handleEntity("response as expected for this handler");
}
if (invocationOnMock.getArgumentAt(1, XYZResponseHandler.class) instanceof XYZResponseHandler) {
return new XYZResponseHandler().handleEntity("response as expected for this handler");
}
return new HttpOkResponse(HttpStatus.SC_OK, "default response");
};
最后,最后,这个解决方案是针对我的测试场景的。