如何在easymock中模拟一个方法,使其返回其中一个参数?

32

我想模拟以下方法:public Object doSomething(Object o);,让它返回传入的参数。我尝试了以下代码:

Capture<Object> copyCaptcher = new Capture<Object>();
expect(mock.doSomething(capture(copyCaptcher)))
        .andReturn(copyCatcher.getValue());

但是一直没有成功,我只得到了一个断言错误:java.lang.AssertionError: Nothing captured yet。有什么建议吗?

5个回答

30

最简单的方法是在IAnswer实现中使用Capture...如果需要内联,当然必须声明为final

MyService mock = createMock(MyService.class);

final Capture<ParamAndReturnType> myCapture = new Capture<ParamAndReturnType>();
expect(mock.someMethod(capture(myCapture))).andAnswer(
    new IAnswer<ParamAndReturnType>() {
        @Override
        public ParamAndReturnType answer() throws Throwable {
            return myCapture.getValue();
        }
    }
);
replay(mock)

这可能是最准确的方法,不需要依赖于某些上下文信息。每次都能对我起作用。


我很喜欢Remi Fouilloux的这篇文章,经常使用它。它消除了需要一个捕获对象的必要性。 - Jan
3
很棒的答案。使用Java 8 lambda表达式,整个IAnswer匿名子类可以重写为“myCapture :: getValue”。 - Henno Vermeulen

19
我正在寻找相同的行为,最终编写了以下内容:
import org.easymock.EasyMock;
import org.easymock.IAnswer;
/** * 使捕获的参数能够回答期望。 * 例如,工厂接口定义如下内容: * <pre> * CharSequence encode(final CharSequence data); * </pre> * 为了测试,我们不需要实现此方法,因此它应该被模拟。 * <pre> * final Factory factory = mocks.createMock("factory", Factory.class); * final ArgumentAnswer<CharSequence> parrot = new ArgumentAnswer<CharSequence>(); * EasyMock.expect(factory.encode(EasyMock.capture(new Capture<CharSequence>()))).andAnswer(parrot).anyTimes(); * </pre> * 创建于2010年6月22日。 * */ public class ArgumentAnswer<T> implements IAnswer<T> {
private final int argumentOffset;
public ArgumentAnswer() { this(0); }
public ArgumentAnswer(int offset) { this.argumentOffset = offset; }
/** * {@inheritDoc} */ @SuppressWarnings("unchecked") public T answer() throws Throwable { final Object[] args = EasyMock.getCurrentArguments(); if (args.length < (argumentOffset + 1)) { throw new IllegalArgumentException("在偏移量 " + argumentOffset + " 处没有参数"); } return (T) args[argumentOffset]; }
}

我在类的javadoc中写了一个快速的“如何”。

希望能帮到你。


谢谢!虽然我已经更改了原始单元测试,但我相信我将再次遇到这个问题!(也许您想直接将其贡献给EM?) - Jan
4
在你的javadoc示例中,“The Capture”是一个误导性的信息 - 它不是必需的:EasyMock.expect(factory.encode(anyObject())).andAnswer(parrot).anyTimes(); - thetoolman
不幸的是,版主们决定删除具有误导性和未使用的“Capture”编辑“完全多余或者会损害可读性”,并且这并不“努力保护帖子所有者的目标”。 - AndrewF

17

捕获变量用于测试后续传递给模拟对象的值。如果你只需要模拟对象返回一个参数(或从参数计算出的某个值),只需实现IAnswer接口。

如果您想要一种可重复使用的方法来传递参数X,请参见"Remi Fouilloux"的实现,但请忽略他在示例中使用的Capture。

如果您只想像"does_the_trick"的示例一样将其内联,则此处的Capture是一个转移注意力的因素。这里是简化版本:

MyService mock = createMock(MyService.class);
expect(mock.someMethod(anyObject(), anyObject()).andAnswer(
    new IAnswer<ReturnType>() {
        @Override
        public ReturnType answer() throws Throwable {
            // you could do work here to return something different if you needed.
            return (ReturnType) EasyMock.getCurrentArguments()[0]; 
        }
    }
);
replay(mock)

不完全是,我的观点是在“Remi Fouilloux”的代码中,“Capture the javadoc”示例是不必要的。在“does_the_trick”的改进后的示例代码中也是不必要的。 - thetoolman

10

基于 @does_the_trick 的方法,使用 lambda 表达式,现在你可以编写如下代码:

MyService mock = EasyMock.createMock(MyService.class);

final Capture<ParamAndReturnType> myCapture = EasyMock.newCapture();
expect(mock.someMethod(capture(myCapture))).andAnswer(() -> myCapture.getValue());

或者按照 @thetoolman 建议的那样不进行捕获

expect(mock.someMethod(capture(myCapture)))
.andAnswer(() -> (ParamAndReturnType)EasyMock.getCurrentArguments()[0]);

感谢EasyMock.newCapture(),现在似乎不能像其他示例中那样使用new实例化Capture了;因为它是一个私有构造函数。 - k-den

2

如果我正确理解了您的问题,那么我认为您可能过于复杂化了它。

Object someObject = .... ;
expect(mock.doSomething(someObject)).andReturn(someObject);

这应该完全没有问题。请记住,您需要同时提供期望的参数和返回值。因此,在两个地方使用同一个对象是可行的。


我不知道“someObject”。它是在我想要测试的方法中实例化的。考虑一个方法“createImage(InputStream image)”(切),它内部调用了“Image filter(Image imge)”(模拟)。 - Jan
啊,好的。那么你可以做几件事情。首先,你可以使用isA()参数匹配器测试对象是否为特定类。其次,我建议编写自己的参数捕获器。我虽然没有做过这个,但我已经编写了自己的参数匹配器。如果你想检查Bean属性,这真的非常有用。不幸的是,我没有那段代码了,但是如果你看一下编写一个匹配器的示例,就不难。 - drekka
你的代码是有效的,但它没有回答使用其中一个参数的问题-它正在使用一个已知对象。 - thetoolman

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