从单元测试到集成测试的有效转换

本文关键字:有效 转换 集成测试 单元测试 | 更新日期: 2023-09-27 18:07:15

我目前正在研究如何在即将到来的项目中执行我们的测试。为了在开发过程的早期发现bug,开发人员会在编写实际代码之前编写单元测试(TDDish)。单元测试将专注于单元(在这种情况下是一个方法)的隔离,因此依赖关系将被模拟等等。

现在,我还想测试这些单元,当它们与其他单元交互时,我认为应该有一个有效的最佳实践来做这件事,因为单元测试已经写好了。我的想法是,单元测试将被重用,但模拟对象将被删除并替换为真实对象。我现在的不同想法是:

  • 在每个测试类中使用全局标志来决定是否应该使用模拟对象。这种方法需要几个if语句
  • 使用一个工厂类,创建一个"instanceWithMocks"或"instanceWithoutMocks"。这种方法对于新开发人员来说使用起来可能比较麻烦,并且需要一些额外的类
  • 将集成测试与不同类中的单元测试分开。然而,这将需要大量的冗余代码,并且维护测试用例将是工作量的两倍。

在我看来,所有这些方法都有优点和缺点。这些方法中哪一个更受欢迎,为什么?是否有更好的方法从单元测试有效地过渡到集成测试?或者这通常是用其他方式完成的?

从单元测试到集成测试的有效转换

我会选择第三个选项

  • 将集成测试从单元测试中分离出来类。然而,这将需要大量的冗余代码和维护测试用例的工作量将是原来的两倍

这是因为单元测试和集成测试有不同的目的。单元测试表明单个功能块是独立工作的。集成测试表明,不同的功能块在相互交互时仍然可以工作。

所以对于单元测试,你想要模拟一些东西,这样你就只测试了一个功能。

对于集成测试,尽量少模拟。

我会把它们放在不同的项目中。在我的地方工作得很好的是使用NUnit和Moq有一个单元测试项目。这是在编写代码时编写的TDD。集成测试是Specflow/Selenium,特性文件是在产品所有者的帮助下编写的,因此我们可以验证我们正在交付所有者想要的东西。

这确实会在短期内产生额外的工作,但会导致更少的错误,更容易的维护,并交付符合需求的产品。

我同意大多数其他答案,单元测试应该与集成测试(选项3)分开。

但是我不同意你的反对意见:

[…然而,这(从集成测试中分离单元)将会需要大量冗余代码,维护测试用例的工作量是的两倍。

生成带有测试数据的对象可能会有很多工作,但这可以重构为测试助手类,即ObjectMother,可以从单元和集成测试,因此不需要冗余

在单元测试中,您检查被测试类的不同条件。

对于集成测试,没有必要重新检查每一个这些特殊情况。相反,您要检查组件是否可以一起工作。

例子

你可以对4种不同的抛出异常的情况进行单元测试。对于集成,没有必要重新测试所有4个条件一个与异常相关的集成测试就足以验证集成系统能够处理异常。

像Ninject/Autofac/StructureMap这样的IoC容器可能在这里对您有用。单元测试可以通过容器解析被测系统,无论注册的是模拟对象还是真实对象,这都只是一个注册问题。类似于工厂方法,但是IoC容器就是工厂。新开发人员需要接受一些培训才能理解,但这是任何复杂系统的情况。这样做的缺点是注册场景可能会变得相当复杂,但是如果不进行尝试,很难说任何给定的系统是否复杂。我怀疑这就是你没有找到任何明确答案的原因。

集成测试应该是与单元测试不同的类,因为您正在测试不同的行为。我认为集成测试的方式是,当你试图确保所有东西都能一起工作时,你要执行它们。他们将使用应用程序部分的输入,并确保返回预期的输出。

我认为您混淆了单元测试和集成测试的目的。单元测试用于测试单个类——这是低级API。集成测试是测试类如何协作。这是另一个更高级别的API。通常,您不能在集成测试中重用单元测试,因为它们代表不同级别的系统视图。使用spring上下文可能有助于为集成测试设置环境。

我不确定用真实对象而不是模拟来重用单元测试是实现集成测试的正确方法。

单元测试的目的是验证对象与外部世界隔离的的基本正确性。mock的存在是为了确保这种隔离。如果你用它们代替真正的实现,你实际上会测试一些完全不同的东西——同一对象链的大部分的正确性,并且你要对它进行多次冗余测试

通过将集成测试与单元测试区分开来,您将能够选择您想要验证的系统部分-通常,测试包含配置,I/O,与第三方系统的交互,UI或任何其他单元测试难以覆盖的部分是一个好主意。