简体   繁体   English

使用mockito操纵参数的存根方法

[英]stubbing methods that manipulates parameters with mockito

I have the following situation: 我有以下情况:

class Worker {  
  public Integer somework() {  
      Integer k=0;  
      Helper h= new Helper();  
      h.change(k);  
      return k;  
    }
}

class Helper {
  public void change(Integer k) {
    //k = Some calcs
  }
}

I'm making unitests for Worker and obviously I want to mock Helper class so that his change method will always put 1 into k . 我正在为Worker制作unitests,显然我想要模仿Helper类,这样他的change方法总是将1放入k

My real situation is more complicated but this code represents the issue. 我的实际情况比较复杂,但这段代码代表了问题。 Thanks for help. 感谢帮助。

I have a method with definition like this: 我有一个定义如下的方法:

class Template{
   public void process(Object rootMap, StringWriter out){
 .......   
  }
 }

I will show you how you can change/modify the "out"(StringWriter) reference. 我将向您展示如何更改/修改“out”(StringWriter)引用。

private final Template mocktTemplate = mock(Template.class);
doAnswer(new Answer<StringWriter>() {

            public StringWriter answer(InvocationOnMock invocation)
                    throws Throwable {
                Object[] args = invocation.getArguments();
                if (args[1] instanceof StringWriter) {
                    StringWriter stringWriter = (StringWriter) args[1];
                    stringWriter.write("Email message");
                }
                return null;
            }
        }).when(this.mocktTemplate).process(anyObject(),any(StringWriter.class));

Now when you do make the actual call like: 现在,当您确实进行实际调用时:

msgBodyTemplate.process(model, msgBodyWriter);

the value of Stringbuffer ref in msgBodyWriter will be "Email message"; msgBodyWriter中Stringbuffer ref的值为“Email message”; irrespective of it earlier value. 不论它早期的价值。

I think doAnswer is the best method dealing with void method where the method manipulates the given parameters. 我认为doAnswer是处理void方法的最佳方法,其中方法操纵给定的参数。

doAnswer(new Answer() {
  public Object answer(InvocationOnMock invocation) {
      Object[] args = invocation.getArguments();
      Mock mock = invocation.getMock();
      return null;
  }})
.when(mock).someMethod();

Basically, after getting the arguments you can do whatever modification you want. 基本上,在获得参数后,您可以进行任何您想要的修改。 There's a blog post explaining it a bit. 有一篇博客文章解释了一下。

To @JB Nizet's point, yes, it's good to refactor for testability. 至于@JB Nizet的观点,是的,重构可测试性是件好事。 Often refactoring to make code more testable leads to code that is better for other reasons - separation of concerns and such. 通常,重构以使代码更易于测试会导致代码更好地用于其他原因 - 分离关注点等等。 It's not always possible to do. 这并不总是可行的。 Say it's not your code, or you have some other requirement to leave it alone (because lots of other classes rely on it being the way it is) or whatever. 假设它不是你的代码,或者你有其他要求让它不管它(因为很多其他类依赖于它的方式)或者其他什么。

If I understand what you need to do, I think you can do it with a spy: 如果我明白你需要做什么,我想你可以用间谍来做:

Worker workerUnderTest = new Worker();
Worker spiedWorkerUT = spy(workerUnderTest);
Helper mockHelper = mock(Helper.class);
when(spiedWorkerUT.createHelper()).thenReturn(mockHelper);
Integer actual = spiedWorkerUT.someWork();
verify(mockHelper).change(0);

Then use the spiedWorkerUT instead of the workerUnderTest to run your tests. 然后使用spiedWorkerUT而不是workerUnderTest来运行测试。

It's not always possible to avoid instantiating something you want to mock. 并不总是可以避免实例化你想要模拟的东西。 For that, there is PowerMock . 为此,有PowerMock

Helper mockHelper = mock(Helper.class);
whenNew(Helper.class).withNoArguments().thenReturn(mockHelper);

I would change the method signature and make it take a Helper instance as argument. 我会更改方法签名,并将Helper实例作为参数。 The caller would create the helper and pass it to the somework method. 调用者将创建帮助程序并将其传递给somework方法。 The test would pass a mock helper. 测试将通过模拟助手。

If this isn't possible, at least call a protected factory method to create the helper, and mock this factory method when testing the somework method in order to make it return a mock helper: 如果这是不可能的,至少调用一个受保护的工厂方法来创建帮助器,并在测试somework方法时模拟这个工厂方法,以使它返回一个模拟助手:

class Worker {  
    public Integer somework(){  
        Integer k=0;  
        Helper h= createHelper();  
        h.change(k);  
        return k;  
    }

    // this method may be mocked when testing somework, to return a mock helper.
    protected Helper createHelper() {
        return new Helper();
    }
}

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

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