【问题标题】:Mocking inter-method dependencies模拟方法间依赖
【发布时间】:2010-03-20 19:07:22
【问题描述】:

我最近开始在我的测试中使用模拟对象,但我仍然对它们非常缺乏经验,并且不确定在某些情况下如何使用它们。目前,我正在努力解决如何模拟方法间依赖关系(调用方法 A 会影响方法 B 的结果),以及是否应该对其进行模拟(在使用模拟框架的意义上)?

以 Java 迭代器为例?模拟 next() 调用以返回正确的值很容易,但是如何模拟 hasNext(),这取决于调用了多少次 next()?目前我正在使用 List.Iterator,因为我找不到正确模拟的方法。

Martin Fowler 对 mock 和 stub 的区分在这里起作用了吗?我应该编写自己的 IteratorMock 吗?

还请考虑以下示例。要测试的方法调用 mockObject.setX() 和之后的 mockObject.getX()。有什么方法可以创建这样一个模拟(无需自己编写),它允许 getX 的返回值取决于传递给 setX 的内容?

【问题讨论】:

    标签: java unit-testing mocking


    【解决方案1】:

    在一般情况下,我会遵循上述 djna 的建议,并为这两种方法设置期望值。但是对于给出的示例,我可能不会使用模拟,而是使用 API 创建一个包含我想要的数据的集合,并从中检索迭代器。 (Arrays.asList(...).iterator())。

    编辑:回复:设置/获取 你还用这个对象做什么?如果它是一个值对象(仅获取/设置),我不会嘲笑它。付出了太多的努力,没有收获。如果不是那么使用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】:

    我会设置我的模拟来为 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
    

    【讨论】:

    • Mockito(我正在使用)具有类似的功能,但如果 next() 和 hasNext() 调用不匹配一对一(i.o.w hasNext() 是比 next()) 调用的次数更多。我知道这可能是一个糟糕的实现,但在我的事业中可能是不可避免的。
    • 在 jmock 的情况下,您可以准确指定期望的顺序,因此会检测到过多的 next() 调用。
    • 谢谢,在这种情况下,Mockito 肯定会允许这样做 - 我会看看。我更新了我的问题以包含另一个我认为无法轻易解决的案例,请看一下。
    【解决方案3】:

    如果你想要一个集合,那就用一个。这是一个不值得嘲笑的简单值对象。当您定义自己的域类型并开始在代码中构建语言时,模拟变得更加有趣。然后单个方法做得更多,协议变得不那么啰嗦——这就是为什么这种复杂性是一种测试气味,表明可能存在设计缺陷。

    据记录,jMock(截至今天)支持比 Mockito 更复杂的场景,这就是初始学习曲线更陡峭的原因。

    【讨论】:

      猜你喜欢
      • 2020-09-03
      • 1970-01-01
      • 2020-06-06
      • 2014-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多