具有嵌套依赖项的测试类

时间:2017-08-09 14:10:58

标签: java unit-testing mockito

我正在测试一个具有嵌套(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对象? 或者在这种情况下还有其他解决方案吗?我可以写自己的模拟对象吗?

2 个答案:

答案 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);
 }
}