【问题标题】:stubbing methods that manipulates parameters with mockito使用 mockito 操作参数的存根方法
【发布时间】:2012-01-06 14:42:41
【问题描述】:

我有以下情况:

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

我正在为Worker 制作单元测试,显然我想模拟Helper 类,以便他的change 方法总是将1 放入k

我的实际情况更复杂,但这段代码代表了问题。感谢您的帮助。

【问题讨论】:

    标签: java unit-testing methods mockito void


    【解决方案1】:

    我会更改方法签名并使其以Helper 实例作为参数。调用者将创建助手并将其传递给somework 方法。测试将通过一个模拟助手。

    如果这不可能,至少调用一个受保护的工厂方法来创建助手,并在测试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();
        }
    }
    

    【讨论】:

    • 嗯.. 好的,这是一个选项。但是,如果我无法更改当前代码,或者重构实际设计以简化测试创建是否“正确”?
    • 是的,没错。编写可测试的代码是优秀开发人员工作的重要组成部分。这就是 IOC 框架如此受欢迎的原因:它促进了一种使代码可测试的设计。
    • 谢谢。我会留下这个帖子,也许还有一些选择可以用 mockito 制作我想要的东西,只是为了学习目的。同时我会听从你的建议。
    【解决方案2】:

    就@JB Nizet 而言,是的,为可测试性进行重构是件好事。通常重构以使代码更具可测试性会导致代码由于其他原因而变得更好 - 关注点分离等。这并不总是可以做到的。假设它不是您的代码,或者您有其他要求不要管它(因为许多其他类都依赖于它的本来面目)或其他什么。

    如果我明白你需要做什么,我想你可以用间谍来做:

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

    然后使用 spiedWorkerUT 而不是 workerUnderTest 来运行您的测试。

    避免实例化您想要模拟的东西并不总是可能的。为此,有PowerMock

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

    【讨论】:

    • 感谢您的意见。我实际上试图做的是在从Worker 调用change 方法时强制mockHelper 改变他的行为。从Helper 继承一些mockHelper 并覆盖change 方法很容易实现,但也许mockito 可以在没有新课程的情况下帮助我?
    • 我很困惑。你想改变什么? “更改”是否会更改您传递的参数或更改其他调用的 Helper 状态?
    • 它改变了参数(k
    • 整数是不可变的,所以这是不可能的。真正的方法是否传递了可变的东西?
    【解决方案3】:

    我有一个定义如下的方法:

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

    我将向您展示如何更改/修改“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));
    

    现在,当您进行实际调用时:

    msgBodyTemplate.process(model, msgBodyWriter);
    

    msgBodyWriter 中 Stringbuffer ref 的值将是“Email message”;不管它的早期价值。

    【讨论】:

      【解决方案4】:

      我认为 doAnswer 是处理给定参数的 void 方法的最佳方法。

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

      基本上,在获得参数后,您可以进行任何您想要的修改。有一个blog post 稍微解释一下。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-21
        相关资源
        最近更新 更多