Java mock函数改变它的行为

时间:2017-12-29 18:34:00

标签: java unit-testing mockito spy

我正在为这样的课程编写单元测试:

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

我想在单元测试lowerMethod's时更改upperMethod行为。 例如,在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
}

我认为我的主要问题是如何让这个想法奏效?这样做的最佳方式是什么?

我在这里阅读了多个文档和问题,我的区别是

  1. 如何通过调用传递字符串列表,即如何提取该Object列表的字符串列表?
  2. Mockito.when中,当我使用any()调用时,answer方法中的list append函数始终返回java.lang.NullPointerException

4 个答案:

答案 0 :(得分:3)

当您正在处理间谍时,您应该使用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)));
    });
}

答案 1 :(得分:2)

另一种方法是扩展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;
        }
    }
}

答案 2 :(得分:1)

只是为了说清楚:你应该&#34;间谍&#34;或&#34;模拟&#34;被测试的课程。你&#34;间谍&#34;或&#34;模拟&#34;它的依赖即:提供业务逻辑的其他类您测试的类与之合作。 间谍睾丸类的需要通常指向设计问题

如果此模拟方法的返回值取决于模拟方法的参数,则只需要mock.*Answer()表单。这通常不是你想要的。

通常,您希望为当前测试用例(方法)返回非常具体的配置返回值。因此,您不应该首先在 setup 方法中配置模拟,特别是如果这意味着使用mock.*Anser()

答案 3 :(得分:0)

RestClientService.java的方法-这是核心类,基本上与外部HTTP RESTClient通信。因此,这里要模拟此类(JUnit测试)。

public <T> T execute(HttpUriRequest httpUriRequest, ResponseHandler<? extends    T> responseHandler) throws IOException {
    return this.getClient().execute(httpUriRequest, responseHandler);
  }

因此,在Service级别类中,使用Service方法中的不同处理程序多次调用了上述方法。 因此,基于调用...的方法来定义处理程序行为。

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

最后,最后,此解决方案适用于我的测试场景。