我正在研究一个方法,它可以被认为是另一个已经定义和测试过的方法的专门化。下面是一个示例代码来说明:
public class ProductService {
public void addProduct(Product product) {
//do something
}
public void addSpecialProduct(Product product) {
addProduct(product);
//do something after
}
}
我不想复制我对addProduct
的测试,这已经很复杂了。我想要的是,当我定义addSpecialProduct
的测试时,我只需要确保它在过程中也调用addProduct
。如果这是两个类协作的问题,那么很容易让协作者进行模拟,只验证目标方法是否被调用(必要时存根)。然而,这两个方法属于同一个类。
我现在想的是监视我正在测试的对象,比如:
public void testAddSpecialProduct() {
//set up code
ProductService service = spy(new DefaultProductService());
service.addSpecialProduct(specialProduct);
verify(service).addProduct(specialProduct);
//more tests
}
然而,我想知道这种方法是否在某种程度上违背了单元测试的目的。对这件事的普遍看法是什么?
我认为这取决于您对单元测试的严格程度。在极端的意义上,单元测试应该只测试行为,而不是测试实现。这意味着你需要重复你的测试(或者采纳@chrylis的建议,将公共功能抽象出来给助手)。确保另一个方法被调用是在测试实现。
然而在现实中,我认为你监视实例以确保调用另一个经过良好测试的方法的想法是一个好主意。以下是我的理由:
1)它简化了你的代码
我立刻明白发生了什么事。它告诉我,已经为另一个方法测试过的所有内容现在也将为真,这里是额外的断言。有一天,你可能修改了实现,使另一个方法不被调用,这将导致你的单元测试失败,这是人们通常试图通过不测试实现来避免的。但根据我的经验,当行为无论如何都要改变时,这样的更改更常见。
您可以考虑重构代码。使用策略模式来实际实现添加产品和特殊产品的功能。
public class ProductService {
@Resource
private AddProductStrategy normalAddProductStrategy;
@Resource
private AddProductStrategy addSpecialProductStrategy;
public void addProduct(Product product) {
normalAddProductStrategy.addProduct(product);
}
public void addSpecialProduct(Product product) {
addSpecialProductStrategy.addProduct(product);
}
}
AddProductStrategy
将有2个实现。一个是在你原来的ProductService.addProduct
实现中发生的事情。第二个实现将委托给第一个实现,然后完成所需的额外工作。因此,您可以分别测试每种策略。第二个策略实现只是第一个实现的装饰器。