如何计算通过Junit和Mockito调用一个方法的次数?

时间:2018-08-11 16:32:00

标签: java junit mockito

我有要在ProductService中测试的这种方法:

@Override
    public void validateUpdate(Product product, Product modifiedProduct, List<FieldError> errors) throws AppException {
        if(modifiedProduct == null || product == null) {
            addError(errors, "product", "Product cannot be null");
        }else {
            validateName(modifiedProduct.getName(), errors);
            validateShortDescription(modifiedProduct.getShortDescription(), errors);
            validateDescription(modifiedProduct.getDescription(), errors);
            validateRegularPriceAndPromotionprice(modifiedProduct.getRegularPrice(), modifiedProduct.getPromotionPrice(), errors);
            validateCategory(product.getCategory(), errors);
            validateCategoryMatches(product.getCategory(), modifiedProduct.getCategory(), errors);
            validateStore(product.getStore(), errors);
            validateSku(modifiedProduct.getSku(), errors);
            validateWeight(modifiedProduct.getWeight(), errors);
            validateQuantityInStock(modifiedProduct.getQuantityInStock(), errors);
            validateNotifyLowStock(modifiedProduct.getNotifyLowStock(), errors);
        }
    }

但是我只想创建一个测试来验证是否调用了所有方法。 第一个是查看addError方法是否被调用一次:

@Test
    public void testValidateUpdateProduct() {
        ProductService productService = Mockito.mock(ProductService.class);
        List<FieldError> errors = new ArrayList<FieldError>();
        productService.validateUpdate(null, null, errors);
        Mockito.verify(productService, Mockito.times(1)).addError(errors, "product", "Product cannot be null");
    }

但是我得到:

Wanted but not invoked:
productService.addError(
    [],
    "product",
    "Product cannot be null"
);
-> at ca.edooby.edoobyapi.service.ProductServiceTest.testValidateUpdateProduct2(ProductServiceTest.java:1022)

However, there was exactly 1 interaction with this mock:
productService.validateUpdate(
    ca.edooby.edoobyapi.model.Product@d2ca3a9,
    ca.edooby.edoobyapi.model.Product@2b26d289,
    []
);

1 个答案:

答案 0 :(得分:1)

似乎您正在尝试验证模拟对象的行为。有一个不好的主意有几个原因:

  1. 默认情况下,mockito用存根替换对实际方法的所有调用(不执行任何操作)
  2. 您的测试与该类的内部实现高度相关
  3. 所有被调用的较小的验证方法都必须具有较低的可见性级别(至少是package-private)才能起作用
  4. 这表明该类具有多种职责-理想情况下,您将有很多较小的验证器,然后分别对其进行测试,然后您可以通过自己喜欢的任何方法对组成验证器的单元进行测试(然后可以完全存根子验证器)。

尽管如此,您想要做的是可行的。我将其称为半模拟方法,如上所述,它是一种反模式。

为简单起见,我们说我们有一个这样的类:

public class Baz {
    public void foo() {
        bar();
    }

    public void bar() {
       // stuff
    }
}

foo等效于您的validateUpdate方法,bar等效于从那里调用的几乎所有其他验证器方法。

该测试将从配置半模拟开始:

    Baz baz = mock(Baz.class); 
    doCallRealMethod().when(baz).foo();

这解决了您测试的核心问题-它实际上并未调用原始的validateUpdate方法,因为默认情况下,mockito排除所有方法(它们不会被调用)。

从那时起,测试照常进行:

    baz.foo();
    verify(baz).bar();

它通过了。再说一次,我不是测试者教皇Mockius III,而是某种权威,但总的来说,您应该:

  • 坚持检查代码的输入和输出(它不会使您与实现紧密地联系在一起)
  • 如果您必须模拟/存根,请避免对被测系统执行此操作-这表明设计存在问题,并且该类可以进一步分解

希望有帮助。