EasyMock: Void方法

68

我有一个方法在一个类中返回void,在我想要测试的类的依赖项中。

这个类非常庞大,而我只使用它的这个单一方法。 我需要替换此方法的实现以进行测试,因为我想让它执行不同的操作,并且我需要能够访问此方法接收到的参数。

我无法在EasyMock中找到这样的方法。我认为我知道如何使用Mockito通过使用doAnswer来做到这一点,但是我不想添加另一个库,除非绝对必要。

5个回答

94

如果我正确理解你想要做的事情,你应该能够使用andAnswer()

mockObject.someMethod(eq(param1), eq(param2));
expectLastCall().andAnswer(new IAnswer() {
    public Object answer() {
        //supply your mock implementation here...
        SomeClass arg1 = (SomeClass) getCurrentArguments()[0];
        AnotherClass arg2 = (AnotherClass) getCurrentArguments()[1];
        arg1.doSomething(blah);
        //return the value to be returned by the method (null for void)
        return null;
    }
});

EasyMock用户指南解释:

创建返回值或异常

有时我们希望我们的模拟对象返回一个值或抛出在实际调用时创建的异常。自EasyMock 2.2以来,由expectLastCall()expect(T value)返回的对象提供了方法andAnswer(IAnswer answer),它允许您指定接口IAnswer的实现,用于创建返回值或异常。

IAnswer回调中,通过EasyMock.getCurrentArguments()可用于获取传递给模拟调用的参数。如果您使用这些参数,那么像重新排序参数这样的重构可能会破坏您的测试。您已经被警告了。


23

如果你每次期望该方法被调用时都只是调用void方法,然后在调用replay()之前调用EasyMock.expectLastCall(),EasyMock会“记住”每次调用。

因此,我认为你不需要显式调用expect()(除了lastCall),因为你不期望从void方法中获得任何东西,除了它的调用。

感谢Chris!

“Fun With EasyMock”是StackOverflow用户Burt Beckwith的一篇好博客文章,提供了更多详细信息。值得注意的摘录如下:

基本上我倾向于使用以下流程:

  1. 创建一个mock
  2. 对于每个预期的调用,调用expect(mock.[method call]).andReturn([result])
  3. 对于每个预期的void调用,调用mock.[method call],然后调用EasyMock.expectLastCall()
  4. 调用replay(mock)以从“记录”模式切换到“回放”模式
  5. 按需注入mock
  6. 调用测试方法
  7. 调用verify(mock)以确保发生了所有预期的调用

1
我认为这个答案没有回答问题 - 他不仅想在方法上断言一个期望,而且还想替换它的实现。 - matt b
1
马特说得好,另一方面,这篇文章帮助我意识到为什么 expect 方法在这里并不适用。 - Peter Wippermann

5
如果你只想在以后访问参数,那么你可能也会喜欢EasyMock 2.4中新增的Captures类。您可以使用“Capture”类的实例来代替匹配器。当调用您模拟的方法时,“Capture”实例将存储它所调用的参数。
Capture<ChartPanel> captured = new Capture<ChartPanel>();
// setChartPanel is going to be called during execution;
// we want to verify some things about the ChartPanel
// instance it's invoked with
chartMock.setChartPanel(capture(captured));
replay(chartMock);

ufdm.setChartAnnotater(chartMock);
// afterPropertiesSet triggers the setChartPanel call...
ufdm.afterPropertiesSet();
verify(chartMock);

// verify some things about the ChartPanel parameter our
// mock object was invoked with
assertSame(plot, captured.getValue().getChart().getPlot());

1
您可能想要查看PowerMock。EasyMock基于代理反射API,这意味着一切都是代理,您只能测试接口,因此只能测试非最终方法和类。对于某些人来说,这可能有效,但如果您正在测试构建的世界,则需要更多的功能。
使用PowerMock,Java 5检测API消除了限制。无需编写要测试的对象的模拟对象实现(仅我的意见),将PowerMock与Mockito(或JMockit)结合使用,您真的会得到更好的体验。
当然,还有另一种重写代码以更轻松地进行测试的方向,如果可能的话,这通常也是一个好主意。

-1
在这种情况下,我发现在我的单元测试类中创建一个嵌套类,并以这种方式重写具有特殊要求的方法是最好的选择。因此,如果您正在测试具有所需参数的该方法的ClassA,则应执行以下操作:
class MockClassA extends ClassA {
    @Override
    void specialMethod(String param1, String param2) {
        // do logging or manipulation of some sort
        super.specialMethod(param1,param2); // if you need to
    }
}

在我的单元测试代码中,我只需使用这个实例即可。把它当作任何其他模拟对象来处理。比混合库容易得多,我同意这可能不是一个好主意。

1
已经考虑过了。但它是一个抽象类,所以我需要提供大约20个空方法实现或者子类化它的某个子类,这可能会让其他开发人员在查看测试时感到有些困惑。 - Iker Jimenez
哦,好的。我不知道它是抽象的。如果你正在使用一个IDE(我假设你正在使用Eclipse或NetBeans),它可以为你提取所有这些抽象方法,然后你只需要在它们上面加上注释来解释你正在做什么。虽然这不是一个非常程序化的解决方案,但至少其他开发人员不会被困惑。 - Marc W

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接