我最近开始在我的测试中使用模拟对象,但我仍然对它们缺乏经验,并且在某些情况下不确定如何使用它们。目前我正在努力解决如何模拟方法间依赖关系(调用方法A对方法B的结果有影响),以及它是否应该被模拟(在使用模拟框架的意义上)?
以Java Iterator为例?模拟next()调用以返回正确的值很容易,但是我如何模拟hasNext(),这取决于next()被调用了多少次?目前我正在使用List.Iterator,因为我找不到正确模拟它的方法。
Martin Fowler在模拟和存根之间的区别是否会在这里发挥作用?我应该写自己的IteratorMock吗?
另请考虑以下示例。要测试的方法在mockObject.getX()上调用mockObject.setX()和稍后的函数。有没有什么方法可以创建这样的模拟(不写我自己的),这将允许getX的返回值依赖于传递给setX的内容?
答案 0 :(得分:2)
我会设置我的模拟为hasNext()和next()提供预期的行为。
jmock等库允许您为同一方法的调用指定不同的行为。在概念上你会说一些诸如
之类的东西for the first call to hasNext() return true
for the first call to next() return this record
for the second call to hasNext() return true
for the second call to next() return this record
for the third call to hasNext() return false
答案 1 :(得分:2)
在一般情况下,我会遵循上述djna的建议并设定对这两种方法的期望。但是对于给出的示例,我可能不会使用模拟,而是使用API来创建包含我想要的数据的集合,并从中检索迭代器。 (Arrays.asList(...).iterator()
)。
编辑:Re:设置/获取 你还用这个对象做了什么?如果它是一个值对象(只获取/设置),我不会嘲笑它。努力没有收获。如果不是那么使用easyMock:
import org.apache.commons.lang.mutable.MutableInt;
import org.easymock.IAnswer;
import org.junit.Test;
import static org.easymock.EasyMock.*;
public class TestSet {
@Test
public void testSetGet() {
final MutableInt value = new MutableInt();
GetSetId id = createMock(GetSetId.class);
id.setID(anyInt());
expectLastCall().andAnswer(new IAnswer<Object>() {
@Override
public Object answer() throws Throwable {
Object[] arguments = getCurrentArguments();
value.setValue((Integer) arguments[0]);
return null;
}
});
expect(id.getID()).andAnswer(new IAnswer<Integer>() {
@Override
public Integer answer() throws Throwable {
return value.toInteger();
}
});
replay(id);
id.setID((int) (Math.random() * 100.0));
System.out.println(id.getID());
verify(id);
}
}
interface GetSetId {
public int getID();
public void setID(int id);
}
答案 2 :(得分:0)
如果你想要一个集合,那就用一个。这是一个简单的价值对象,不值得嘲笑。当您定义自己的域类型并开始在代码中构建语言时,模拟会变得更有趣。然后个别方法做得更多,协议变得不那么健谈 - 这就是为什么这种复杂性是一种测试气味,表明可能存在设计缺陷。
为了记录,jMock(截至今天)支持比Mockito更复杂的场景,这就是为什么初始学习曲线更陡峭。