2014-10-07 30 views
1

我有一个需要编写测试的大程序。我想知道编写以特定顺序运行的测试是否是错误的,因为某些测试必须按顺序运行,并且依赖于以前的测试。单元测试的顺序是否令人沮丧?

例如像下面这样的场景:

  • CreateEmployer
    • 在CreateEmployee(要求雇主)
    • 添加部门

我看到的缺点这种方法如果一次测试失败,所有的后续测试也将失败。但是,我将不得不编写代码来构建数据库,所以使用构建模拟数据库的代码作为一种类似集成的单元测试可能是更有效的方法。

我应该创建数据库而不使用测试作为种子方法,然后再次运行每个方法以查看结果吗?我用这种方法看到的问题是,如果种子方法不起作用,则所有测试都将失败,并且不会立即明确错误在种子方法中,而不是服务或测试本身。

回答

2

是的,这是不鼓励。测试不应该“暂时耦合”。

每个测试应该完全隔离其他测试运行。如果您在由试验A创建的工件被测试B需要的情况下发现自己那么你有两个问题要解决:

  • 试验A不应创建工件(或副作用)。
  • 测试B应该使用模拟数据作为其“排列”步骤的一部分。

基本上,单元测试不应该使用真正的数据库。他们应该测试代码的逻辑,而不是与基础架构依赖关系的交互。模拟这些依赖关系以便测试代码。

+0

有道理。我想我所描述的将是更多的集成测试。假设单元测试不测试与数据库的交互并且仅测试逻辑,我可以想到几个我真正需要测试的地方。测试什么本质上是setter函数'this.Department = foo'似乎很浪费。我会说大约2%的代码具有不平凡的逻辑。 – 2014-10-07 18:19:30

+1

@RaySülzer:那么,你不会直接*测试简单的制定者。您将测试业务操作,并且在这些操作中执行的逻辑将调用setter。 (如果他们*不*调用setter,那么您的业务操作*都不使用*这些setter,并且setter应该完全从代码中移除。)对于集成测试,您希望完全从测试中移除逻辑并只测试集成点本身(通常是DAL操作)。这里有一些选择,取决于什么适用于你的团队。但时间耦合仍应该被删除。 – David 2014-10-07 18:22:00

+0

谢谢!我的大多数服务调用都是简单的setter,而不是调用_orm.Update(object)。只有我的一些功能确实需要需要联合测试的业务逻辑。这是一个非常基本的程序。 – 2014-10-07 18:23:26

2

让单元测试相互依赖是个不错的主意。在您的示例中,您可能在CreateEmployer和AddDepartment中都存在缺陷,但是当所有三个都因为CreateEmployer测试而失败时,您可能错误地认为只有CreateEmployer测试“真的”失败。这意味着你已经失去了AddDepartment失败的有价值的信息。

另一个问题是,您可能会在将来调用AddDepartment时未创建单独的工作流而不调用CreateEmployer。现在,您的测试假定CreateEmployer将始终被调用,但实际上并非如此。所有三个测试都可以通过,但应用程序仍然可能会中断,因为您有一个您不知道的依赖关系。

最好的测试不会依赖于数据库,而是会允许您手动指定或“模拟”数据。那么你不必担心无关的数据库问题会打破你的所有测试。

1

如果这些都是真正的单元测试,那么是的,要求特定的订单是一个不好的做法,有几个原因。

  1. 耦合 - 正如你指出的那样,如果一个测试失败,那么后面的测试也会失败。这将掩盖真正的问题。
  2. TDD - TDD的核心原理是使测试更容易运行。如果你这样做,开发人员更可能运行它们。如果它们很难运行(例如,我必须运行整个套件),那么它们不太可能被运行并且其价值被丢失。
1

理想情况下,单元测试不应该依赖于另一个测试的完成才能运行。按照给定顺序运行它们也很困难,但这可能取决于您的单元测试工具。

在您的示例中,我将创建一个测试,测试CreateEmployer方法并确保它按照您的期望返回新对象。

我要创建的第二个测试将是CreateEmployee,如果该测试需要使用依赖注入的Employer对象,则您的CreateEmployee方法可能会收到其Employer对象。这是您将使用模拟对象(创建代码,返回固定/已知雇主)的地方,因为Employer对象将使用CreateEmployee方法。这使您可以使用Employer对象的给定/已知实例来测试CreateEmployee方法及其对该对象的操作。

你的第三个测试,AddDepartment,我假设还取决于一个雇主对象。这种单元测试可以遵循相同的模式,并在测试期间接收消费者模拟的雇主对象。 (与上述单元测试相同的对象)。

每个测试现在单独运行/失败,并且可以按任意顺序运行。