【问题标题】:How to test a method that depends on another already tested method?如何测试依赖于另一种已测试方法的方法?
【发布时间】:2016-12-30 06:13:31
【问题描述】:

我正在研究一种方法,该方法可以被视为另一种已定义和测试方法的特化。下面是一个示例代码来说明:

public class ProductService {

    public void addProduct(Product product) {
        //do something
    }

    public void addSpecialProduct(Product product) {
        addProduct(product);
        //do something after
    }

}

我不想复制我对addProduct 的测试,这些测试已经非常复杂。我想要的是当我为addSpecialProduct 定义测试时,我只是确保它在此过程中也调用addProduct。如果这是 2 个类协作的问题,那么很容易模拟协作者并验证目标方法是否被调用(并在必要时将其存根)。但是,这两个方法属于同一个类。

我现在的想法是监视我正在测试的对象,例如:

public void testAddSpecialProduct() {
    //set up code
    ProductService service = spy(new DefaultProductService());    
    service.addSpecialProduct(specialProduct);
    verify(service).addProduct(specialProduct);
    //more tests
}

但是,我想知道这种方法是否以某种方式违背了单元测试的目的。对此问题的普遍共识是什么?

【问题讨论】:

  • 您能否将addProduct 方法的断言放在测试实用程序方法中并在多个测试用例之间重用?
  • 这个类对我来说听起来有点代码味道,最好将这 2 个功能(产品和特殊产品)组合成 2 个单独的类(然后你的测试变得明显和容易)。拥有通用的“添加”和“专门的添加”会破坏单一责任。
  • @chrylis 这实际上听起来很合理,是的。我忘记了我以前做过类似的事情。
  • @kuhajeyan 不是。想象一下:addProduct 基本上是添加产品的简单服务。 addSpecialProduct 是一项添加产品然后执行其他操作的服务,例如触发警报/通知等。
  • 您在addProduct 中测试过的所有案例是否也适用于addSpecialProduct,或者只是其中的一部分?

标签: java unit-testing mockito


【解决方案1】:

我认为这取决于您对单元测试的要求有多严格。从极端的意义上说,单元测试应该只测试行为,而不是实现。这意味着您需要复制您的测试(或采用@chrylis 的建议,将通用功能抽象为帮助程序)。确保调用另一个方法是测试实现。

但实际上,我认为您监视实例以确保调用其他经过良好测试的方法的想法是个好主意。以下是我的理由:

1) 它简化了您的代码。

2) 我立即明白发生了什么。它告诉我,已经为其他方法测试过的所有内容现在也将是正确的,这是额外的断言。

有一天,您可能会修改实现以便不调用其他方法,这将导致您的单元测试失败,这通常是人们通过不测试实现来避免的。但根据我的经验,当行为无论如何都会发生变化时,这样的变化更为常见。

【讨论】:

    【解决方案2】:

    您可以考虑重构您的代码。使用策略模式来实际实现添加产品和特殊产品的功能。

    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 实现中发生的事情。第二个实现将委托给第一个实现,然后执行所需的额外工作。因此,您可以分别测试每个策略。第二个策略实现只是第一个实现的装饰器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多