我正在测试一个具有嵌套(Autowired)依赖项的类。该类实现了businesslogic,用于在后端进行更改。具体来说,测试应断言当某个后端调用返回错误时:
我不知道后者是怎么做的。 我的班级看起来像这样:
public class Handler {
@Autowired
private DaoImpl dao;
@Autowired
private SpecificUtil util1;
@Autowired
private GeneralUtil util2;
@Autowired
private Helper helper;
public Response doSomethingClever(Request request) {
// calls to dao
// logic with help of util and helper classes
}
}
测试类:
public class HandlerTest {
@Spy
private DaoImpl dao;
@Mock
private SpecificUtil util1;
@Mock
private GeneralUtil util2;
@Mock
private Helper helper;
@InjectMocks
Handler handler;
@Test
public void testDoSomethingClever() {
// set up dao to fail
QueryResult error = getErrorResult();
org.mockito.Mockito.when(dao.queryBackEnd(any(SpecificQuery.class))).thenReturn(error);
// perform query
Request request = getTestRequest();
Response errorResponse = handler.doSomethingClever(request);
// verify that either:
// Response has errors - fails
// because helper classes are mocks, have not set the error
assertNotNull(response.getErrorMessage());
// the method setErrors of Response was called once - fails
//because the setError was called earlier!
Response spyResponse = Mockito.spy(errorResponse);
verify(spyResponse, times(1)).setError(anyString);
//verify no other calls are made except the queryBackEnd call - this part works
org.mockito.Mockito.verify(dao).queryBackEnd(any(SpecificQuery.class));
org.mockito.Mockito.verifyNoMoreInteractions(dao);
}
}
Response对象是在Handler类中创建的。如果我检查返回的响应,Mockito将不记录任何交互,因为在调用Mockito.spy之前已经进行了交互。
我尝试使用@Spy代替@Mock进行集成测试。我们的想法是实例化除dao之外的所有嵌套依赖项,并获得适当的响应来测试错误。但这不起作用,因为某些@Autowired帮助程序和实用程序类也具有@Autowired依赖项,并且这些嵌套依赖项在测试期间未实例化。
有没有办法用Mockito将@Spy对象注入其他@Spy对象? 或者在这种情况下还有其他解决方案吗?我可以写自己的模拟对象吗?
答案 0 :(得分:1)
单元测试应仅测试特定单元的代码(此处为Handler
类)。这包括与依赖项进行交互。
根据您在问题和评论中所写的内容,handle
方法看起来就像这样:
public class Handler {
@Autowired
private DaoImpl dao;
@Autowired
private Util util;
public Response doSomethingClever(Request request) {
SpecificQuery specificQuery = new SpecificQuery();
specificQuery.setSomeData(request.getSomeData());
IntermidiateResponse intermidiateResponse = dao.queryBackEnd(specificQuery);
Response response = util.processIntermidiateResult(intermidiateResult);
return response;
}
}
这里有一些单元测试的互动:
Request
实例声明DaoImpl::queryBackEnd
方法使用SpecificQuery
实例调用someData
实例,someData
属性设置为Request
属性{{1}对象IntermidiateResponse
方法返回的模拟DaoImpl::queryBackEnd
声明此结果已传递给Util::processIntermidiateResult
方法Response
方法返回的模拟Util::processIntermidiateResult
声明这正是从handle
方法返回的内容这样,您可以100%覆盖Handler::handle
方法。如果您在响应处理管道中有更多的调用,则相应地测试它们。
希望这能回答你的问题。祝你好运
答案 1 :(得分:0)
有两个选项,一个是单独测试Handler,嘲笑其他所有东西。请参阅answer by jannis。另一个选项是控制/模拟Handler的输入和输出,并将Handler 和的所有实用程序类视为黑盒子。
我选择了最后一个选项,因为这意味着我可以重构实用程序类和Handler类本身,只要我不改变Handler的功能,测试就会成功。 意味着我已经离开了一些单元测试并且我真的做了更多的集成测试。
为此我嘲笑Dao类,以便我可以让它在所需的点返回一个错误,以便我可以断言在错误之后没有进一步的调用。 它是我模拟的唯一类,所以我需要将它注入到Handler中。这可以使用Springs ReflectionTestUtils(我昨天对此有所了解)
然后测试代码变短:
public class HandlerTest {
@Autowired
private Handler handler;
@Test
public void testDoSomethingClever() {
// set up dao to fail
Dao mockDao = org.mockito.Mockito.mock(DaoImpl.class);
QueryResult error = getErrorResult();
org.mockito.Mockito.when(dao.queryBackEnd(any (SpecificQuery.class))).thenReturn(error);
// inject the dao
ReflectionTestUtils.setField(handler, "dao", mockDao);
// perform query
Request request = getTestRequest();
Response errorResponse = handler.doSomethingClever(request);
// verify that Response has errors
assertNotNull(response.getErrorMessage());
//verify no other calls are made except the queryBackEnd call
org.mockito.Mockito.verify(dao).queryBackEnd(any(SpecificQuery.class));
org.mockito.Mockito.verifyNoMoreInteractions(dao);
}
}