【问题标题】:verify void method call验证 void 方法调用
【发布时间】:2017-04-10 17:40:37
【问题描述】:

我有以下课程:

public class A {
    private Field field;
    public A(Field field){
        this.field = field;
    }
    public add(int size){
        field.addBall(new Ball(size));
    }
}

public interface Things {
    List<Ball> ballList = new LinkedList<>();
    public addBall(Ball b){
        ballList.add(b);
    }
}

我想测试 A 类的 add() 方法。更具体地说,我想测试是否调用了 Things 类的 addBall() 方法。 这个测试失败说: 验证期望失败:Things.addBall(...) : expected: 1 ,actual: 0;

public class TestA {
        private Things thing;
        private A a;
        @Before
        public void setUp() {
            thing = EasyMock.createNiceMock(Things.class);
            a = new A(thing);
        }

        @After
        public void tearDown() {
        }
    @Test
        public void addTest(){
            thing.addBall(new Ball(345));
            EasyMock.expectLastCall();
            EasyMock.replay(cache);
            a.add(345);
        EasyMock.verify(cache);
        }
}

什么是正确的方法?这个测试有什么问题?

【问题讨论】:

    标签: java testing junit mocking easymock


    【解决方案1】:

    我通常使用 Mockito,但这是我的猜测:

    当 EasyMock 将您传递到录制阶段的new Ball(345) 的结果与a.add(345) 内部的new Ball(size) 的结果进行比较时,您看到的是两个完全不同的Ball 对象。他们不比较equal(),虽然大小是一样的。

    作为一般规则,对new 的显式调用会在您测试时出现问题。一种可能的解决方案是引入BallFactory。然后您可以验证是否调用了 factory.createBall(345),其结果是 Ball 的模拟是传递给 addBall() 的模拟

    (这里有一篇关于这个主题的优秀文章:http://misko.hevery.com/2008/07/08/how-to-think-about-the-new-operator/

    如果你想放宽你的测试要求,只检查addBall()是否被任何对象调用(我建议不要这样做),你可以使用EasyMock的anyObject()方法来匹配任何Ball

    【讨论】:

    • 我不比较球我只测试 addBall() 是否被调用。
    • EasyMock 比较记录的参数,而不仅仅是调用的事实。 (见我的编辑)
    • Arkady 是正确的;您的 Ball 类需要 @Override equals() 以使该测试按现在编写的方式工作。
    • 哦,我明白了,这是我能想象到的最糟糕的情况。不想粘贴原始代码,因为它更复杂但很好。有没有办法不比较论点?回答男士们。
    • 请查看答案底部的方法来匹配任何参数值(这应该与根本不检查相同)。
    【解决方案2】:

    我假设名为cache 的变量实际上是thing。那么,另一个答案是对的。默认情况下,参数使用等于匹配器。你可能还没有在Ball 上定义equals。

    我做了不同的实现来展示如何在不添加equals 方法的情况下解决这个问题。

    关于代码的一些cmets:

    • 这里不需要漂亮的模拟。普通的模拟实际上更安全
    • 我喜欢将静态导入用于我的测试代码 :-)
    • expectLastCall 不是必需的。默认情况下,它是 void 方法
    • 我修复了很多编译错误。下次尝试给出工作示例

    先固定代码:

    public class Things {
      List<Ball> ballList = new LinkedList<>();
    
      public void addBall(Ball b){
        ballList.add(b);
      }
    }
    
    public class A {
      private Things field;
    
      public A(Things field){
        this.field = field;
      }
    
      public void add(int size){
        field.addBall(new Ball(size));
      }
    }
    

    我在Ball 中添加了getSize 以提供实际示例。

    public class Ball {
    
      private final int size;
    
      public Ball(int size) {
        this.size = size;
      }
    
      public int getSize() {
        return size;
      }
    }
    

    最后,TestA

    public class TestA {
      private Things thing;
      private A a;
    
      @Before
      public void setUp() {
        thing = createMock(Things.class);
        a = new A(thing);
      }
    
      @Test
      public void addTest(){
          // You should keep only one of these examples
          // 1. You don't care about checking `345`. You just want to make sure there is a call. `anyObject` is exactly what you need
         thing.addBall(anyObject());
         // 2. You want to expect a `Ball` with `345` 
         thing.addBall(cmp(new Ball(345), Comparator.comparingInt(Ball::getSize), LogicalOperator.EQUAL));
        // 3. You prefer a simpler check possibly on many attributes. Just capture the `Ball` passed in parameter and check after if it contains what you want
        Capture<Ball> capture = Capture.newInstance();
        thing.addBall(capture(capture));
        // Now the original code
        replay(thing);
        a.add(345);
        verify(thing);
        // Assert on the capture is you used solution #3
        assertEquals(345, capture.getValue().getSize());
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-06-12
      • 1970-01-01
      • 1970-01-01
      • 2021-01-02
      • 2013-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多