7

我目前正在研究我们应该如何在即将到来的项目中进行测试。为了在开发过程中及早发现错误,开发人员会在实际代码之前编写单元测试(TDDish)。单元测试将像他们应该的那样,孤立地关注单元(在这种情况下是一种方法),因此依赖关系将被模拟等等。

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

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

在我看来,所有这些方法都有利有弊。其中哪一个是首选,为什么?有没有更好的方法来有效地从单元测试过渡到集成测试?或者这通常以其他方式完成?

4

6 回答 6

5

我会选择第三种选择

  • 将集成测试与不同类中的单元测试分开。然而,这将需要大量冗余代码,维护测试用例将是工作的两倍

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

因此,对于单元测试,您想要模拟事物,以便您只测试一个功能。

尽可能少地进行集成测试模拟。

我会把它们放在不同的项目中。在我这里行得通的是有一个使用 NUnit 和 Moq 的单元测试项目。这在编写代码时写成 TDD。集成测试是 Specflow/Selenium,功能文件是在产品所有者的帮助下在规划会话中编写的,因此我们可以验证我们正在交付所有者想要的东西。

这确实在短期内创造了额外的工作,但会导致更少的错误、更容易的维护和交付匹配要求。

于 2013-04-02T13:25:28.267 回答
1

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

于 2013-04-02T13:20:44.620 回答
1

集成测试应该是与单元测试不同的类,因为您正在测试不同的行为。我认为集成测试的方式是,它们是您在尝试确保一切协同工作时执行的测试。他们将使用应用程序部分的输入,并确保返回预期的输出。

于 2013-04-02T13:26:57.910 回答
1

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

于 2013-04-02T13:29:53.520 回答
1

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

单元测试的目的是在与外界隔离的情况下验证对象的基本正确性。有模拟以确保隔离。如果您将它们替换为实际实现,您实际上最终会测试完全不同的东西 - 同一对象链的大部分的正确性,并且您会多次重复测试它。

通过使集成测试与单元测试不同,您将能够选择要验证的系统部分——通常,测试暗示配置、I/O、与第三方系统交互的部分是一个好主意, UI 或单元测试难以涵盖的任何其他内容。

于 2013-04-02T13:41:03.257 回答
1

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

但我不同意你的相反论点:

[...] 但是,这(将单元与集成测试分开)将 需要大量冗余代码,并且维护测试用例将是工作的两倍

使用测试数据生成对象可能需要做很多工作,但这可以重构为测试助手类,也就是ObjectMother,可以从单元测试和集成测试中使用,因此不需要冗余

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

对于集成测试,没有必要重新检查这些特殊情况。相反,您检查组件是否协同工作。

例子

您可能对 4 种引发异常的不同情况进行了单元测试。对于集成,无需重新测试所有 4 个条件 一个与异常相关的集成测试足以验证集成系统是否可以处理异常。

于 2013-04-02T16:13:10.977 回答