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,
[]
);
解决方案
您似乎正在尝试验证模拟对象的行为。这是一个坏主意有几个原因:
- 默认情况下,mockito 用存根替换对实际方法的所有调用(它什么都不做)
- 您的测试与类的内部实现高度耦合
- 所有被调用的较小的验证方法都必须放宽可见性级别(至少到包私有)才能工作
- 它表明该类具有多种职责-理想情况下,您将拥有许多较小的验证器,然后分别对其进行测试,然后您可以对组成的验证器进行单元测试-通过您喜欢的任何方法(然后您可以完全删除子-验证器)。
不过,您尝试做的是可行的。我称之为半模拟方法,如上所述 - 这是一种反模式。
为简单起见,假设我们有一个这样的类:
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();
它通过了。再说一次,我不是教皇莫克修斯三世,测试者是某种权威,但总的来说,你应该:
- 坚持检查代码的输入和输出(它不会将你与实现联系起来)
- 如果您必须模拟/存根东西,请避免对被测系统这样做 - 这表明设计存在问题,并且可以进一步分解该类
希望有帮助。
推荐阅读
- volttron - 应用“RPC.allow”时如何调用函数?
- java - 如何完成三个Queue实现以及三个Stack实现?
- php - WordPress 在 2 列中显示循环
- scala - Spark sql优化技术将csv加载到hive的orc格式
- python - 无法覆盖自定义字段 __init__ 并进行迁移,抛出错误
- c++ - 了解此 printf 的结果
- python - Postgres 仅在部署到 Heroku(Flask)时才返回意外结果
- javascript - 在 Javascript 对象数组中查找递归(循环)id
- scipy - 是否可以解决 python 的 RegularGridInterpolator 中的输入?
- javascript - 你能用JS移动一个div吗?