验证void方法调用

时间:2017-04-10 17:40:37

标签: java testing junit mocking easymock

我有以下课程:

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);
        }
}

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

2 个答案:

答案 0 :(得分:1)

我通常和Mockito一起去,但这是我的猜测:

当EasyMock将您传递到录制阶段的new Ball(345)的结果与new Ball(size)a.add(345)的结果进行比较时,您会看到两个完全不同的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

答案 1 :(得分:0)

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

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

关于代码的一些评论:

  • 这里你不需要一个好的模拟。正常的模拟事实上更安全
  • 我喜欢测试代码的静态导入: - )
  • 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));
  }
}

我已将getSize添加到Ball以获得实际的示例。

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());
  }
}