简体   繁体   English

具有嵌套依赖项的测试类

[英]Test class with nested dependencies

I'm testing a class with nested (Autowired) dependencies. 我正在测试具有嵌套(自动装配)依赖项的类。 The class implements businesslogic for making alterations in the backend. 该类实现用于在后端进行更改的businesslogic。 Specifically the test should assert that when a certain backend call returns an error: 具体来说,测试应断言某个后端调用返回错误时:

  • No more backend calls are made 不再进行后端调用
  • The response object returns an error 响应对象返回错误

I dont know how the do the latter. 我不知道后者该怎么办。 My class looks something like this: 我的课看起来像这样:

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
    }
}

The testclass: 测试类:

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

} }

The Response object is created in the Handler class. 在Handler类中创建Response对象。 If i check the returned response, no interactions will be recorded by Mockito, because the interactions have taken place prior to calling Mockito.spy. 如果我检查返回的响应,则Mockito将不会记录任何交互,因为交互是在调用Mockito.spy之前发生的。

I have tried making it an integration test, by using @Spy instead of @Mock. 我尝试通过使用@Spy而不是@Mock使其成为集成测试。 The idea is to instantiate all the nested dependencies except for the dao and get a proper Response to test for errors. 这个想法是实例化除dao之外的所有嵌套依赖项,并获得适当的Response以测试错误。 But this does not work because some of the @Autowired helper and utility classes also have @Autowired dependencies and these nested dependencies are not instantiated during testing. 但这是行不通的,因为某些@Autowired帮助程序和实用程序类也具有@Autowired依赖关系,并且这些嵌套的依赖关系在测试期间不会实例化。

Is there any way to inject @Spy objects into other @Spy objects with Mockito? 有没有办法用Mockito将@Spy对象注入到其他@Spy对象中? Or is there some other solution in this case? 还是在这种情况下还有其他解决方案? Sould i write my own mockobjects? 我可以编写自己的模拟对象吗?

A unit test should test the code of a specific unit only (here the Handler class). 单元测试应仅测试特定单元的代码(此处为Handler类)。 This includes interacting with the dependencies. 这包括与依赖项进行交互。

From what you wrote in your question and comments your handle method looks something along these lines: 从您在问题和评论中所写的内容来看, 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;
    }
}

There are a few interactions to unit test here: 这里有一些要进行单元测试的交互:

  • Given a Request instance assert that DaoImpl::queryBackEnd method is called with a SpecificQuery instance that has someData property set to someData property from the Request object 给定一个Request实例,就可以断言使用SpecificQuery实例调用DaoImpl::queryBackEnd方法,该实例具有将Request对象的someData属性设置为someData属性
  • Given a mocked IntermidiateResponse being returned from the DaoImpl::queryBackEnd method assert that this result is passed on to the Util::processIntermidiateResult method 给定一个嘲笑IntermidiateResponse从返回DaoImpl::queryBackEnd方法断言这一结果是到过Util::processIntermidiateResult方法
  • Given a mocked Response being returned from the Util::processIntermidiateResult method assert that this is exactly what gets returned from the handle method 给定从Util::processIntermidiateResult方法返回的模拟Response ,则断言这正是从handle方法返回的内容

This way you have 100% coverage on the Handler::handle method. 这样,您对Handler::handle方法的覆盖率为100%。 If you have some more calls in the response processing pipeline you test them all accordingly. 如果响应处理管道中还有其他一些调用,则可以对它们进行相应的测试。

Hope this answers your question. 希望这能回答您的问题。 Good luck 祝好运

There are two options, one is to test the Handler in isolation, mocking everything else. 有两种选择,一种是单独测试Handler,模拟其他所有东西。 See the answer by jannis . 请参阅jannis答案 The other option is to control/mock the in- and output of the Handler, and treat the Handler and all its utility classes as a black box. 另一个选择是控制/模拟Handler的输入和输出,并将Handler 及其所有实用程序类视为黑匣子。

I have opted for this last option because it means that i can refactor the utility-classes, and the Handler class itself, and that tests will succeed as long as i don't change what the Handler does. 我选择了最后一个选项,因为这意味着我可以重构实用程序类和Handler类本身,并且只要我不更改Handler的功能,测试就可以成功。 It does mean that i have departed somewhat from doing unit testing and that i'm really doing more of an integration test. 确实意味着我已经脱离了单元测试的范围,而我实际上正在做更多的集成测试。

For this i mock the Dao class, so that i can have it return an error at the desired point and so that i can assert that no further calls are made after the error. 为此,我模拟了Dao类,以便可以让它在所需的点返回错误,并可以断言在错误之后不再进行任何调用。 It is the only class i mock, so i need to inject it into the Handler. 这是我模拟的唯一类,因此我需要将其注入到Handler中。 This is possible with Springs ReflectionTestUtils (I did'nt know about this yesterday) Springs ReflectionTestUtils可以做到这一点(我昨天不知道这件事)

The testcode then becomes shorter: 然后,测试代码变得更短:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM