【问题标题】:Is it bad form to count on the order of your NUnit unit tests指望 NUnit 单元测试的顺序是不是很糟糕
【发布时间】:2009-01-30 23:33:31
【问题描述】:

我一直在疯狂地创建单元测试,并发现我经常不得不在一个测试中设置一些我在之前的测试中刚刚拆除的东西。在一次测试(例如插入测试)中创建某些东西(例如数据库记录)然后将其用于以后的测试(例如删除测试)是否合理?还是每个测试都应该完全独立?

您甚至可以确定 NUnit 中的测试顺序还是它们总是按字母顺序进行?

注意:我特别询问 one 测试文件中的测试顺序。 跨测试文件或更全局。

更新:感谢所有回答的人 - 有很多很好的答案,并且小组的感觉非常一致。我选择了 John Nolan 的答案,因为他提供了最完整的解释和大量链接。正如您可能已经猜到的那样,我非常想打破这条规则,尽管我认为它可能像约翰所说的那样有点“臭”。也感谢 Fortyrunner 添加了 unit-testing 标签。

【问题讨论】:

  • 在反馈和解释您的最新想法方面做得很好。非常好。
  • 谢谢,乔恩。我认为这是一个社区,如果社区要发展壮大,就需要一定程度的关怀。

标签: unit-testing nunit


【解决方案1】:

查看test fixture setups,它允许您指定将在夹具中的任何测试之前执行的函数。这允许您进行一次通用设置,并且无论您运行一个测试还是套件中的所有测试,它都会始终运行。

【讨论】:

    【解决方案2】:

    依赖测试的顺序表明您正在跨测试保持状态。这是smelly

    一种更简洁的测试方式是您只依赖要检查其行为的单个功能。通常你 mock 需要让你的方法在测试下运行的其他对象。

    考虑进行单元测试的一个好方法是Arrange, Act, Assert 模式。

    以下是 Karl Seguin 出色的免费 eBook 的 sn-p。我已经注释了 Arrange、Act 和 Assert。

    [TestFixture] public class CarTest 
    { 
        [Test] public void SaveCarCallsUpdateWhenAlreadyExistingCar()   
        {
             //Arrange
             MockRepository mocks = new MockRepository();
             IDataAccess dataAccess = mocks.CreateMock<IDataAccess>();   
             ObjectFactory.InjectStub(typeof(IDataAccess), dataAccess); 
             //Act
             Car car = new Car(); 
             Expect.Call(dataAccess.Save(car)).Return(389); 
             mocks.ReplayAll(); 
             car.Save(); 
             mocks.VerifyAll(); 
             // Assert
             Assert.AreEqual(389, car.Id); 
             ObjectFactory.ResetDefaults();
        } 
    }
    

    【讨论】:

    • 指令 ObjectFactory.ResetDefaults() 实际上是“断言”的一部分吗?我已经考虑了一些,我认为它应该被称为 Arrange, Act, Assert, Clean or Arrange, Act, Assert, Reset?类似的东西,或者可能:安排、行动、断言、混乱?
    • 虽然我基本同意你的观点,但在某些情况下,一个测试必须依赖于另一个测试和/或使用模拟对象可能会破坏测试过程的要点。一个例子是一个团队需要将函数拆分成比合理的更谨慎的部分,或者在数据库后端版本之间进行测试时。在这些情况下,我喜欢使用静态变量,这些变量最初可以设置,然后可用于所有后续函数。
    • @Brain2000 我建议这些不是传统意义上的“单元”测试。我不太清楚你所说的“功能被分成比合理的更谨慎的部分”是什么意思,但在后一种情况下,你似乎更多地谈论交互/集成测试。这些没有什么问题,但它们执行不同的工作。
    • @JohnNolan 我认为“拆分为比合理的更谨慎的部分”的示例将是一个测试,该测试使用每个数据库字段的一个单元测试函数来验证数据库中的正确记录创建。假设有 50 个字段,所以有 50 个单元测试。对我来说,检索一次记录并在所有测试函数中使用相同的对象会更有意义,而不是检索相同的记录 50 次。
    【解决方案3】:

    单元测试旨在独立运行,而不是作为顺序脚本运行。如果您确实需要它们按顺序运行,请将它们收集到单个测试函数中。

    如果您的单元测试受到expensive set-up 的影响,那么当您认为自己正在执行单元测试时,您可能正在执行集成测试。如果您在大多数单元测试中使用 SQL 数据库,那么您实际上是在与数据访问层进行集成测试。

    【讨论】:

      【解决方案4】:

      我认为每个测试都完全独立于任何其他测试。即使您可以规定测试的顺序,当测试必须更改时,这将是一场维护噩梦。

      【讨论】:

        【解决方案5】:

        我真的不会依赖测试的顺序。相反,我会将通用设置代码放入一个单独的方法中,并从简单测试和更复杂的测试中调用它。或者,只需在删除测试开始时调用插入测试本身。

        【讨论】:

          【解决方案6】:

          我强烈建议您让所有单元测试独立。

          您的业务逻辑/数据库结构等可能会随着时间而改变,因此您最终将不得不替换或重写(甚至丢弃)现有的单元测试 - 如果您还有其他几项测试,则取决于您的测试。重新更换,这可能会导致不必要的麻烦,因为您还必须通过所有其他测试并检查它们是否仍然按预期工作。

          此外,一个失败的单元测试不应拖累许多其他单元测试(可能完全独立运行)。

          【讨论】:

            【解决方案7】:

            不幸的是,单元测试的运行顺序是不可预测的,或者至少将来可能会改变。例如。单元测试框架将被更改,因此每个测试都将在单独的线程中执行。 所以从我的角度来看,使用测试顺序是不合理的。 另一方面,您可以创建一组小型独立测试来测试代码的一小部分,然后创建一个或多个大型测试,以特定顺序运行您的小型测试。

            【讨论】:

              【解决方案8】:

              如果您有状态测试(数据库工作的一个常见问题 - 这是我不在 SO 时所做的事情),那么在我看来,避免测试文件中的顺序并不是绝对必要的。但是,您必须认识到,如果您有 2 个测试,其中测试 2 取决于测试 1 通过,那么如果测试 1 失败,您将获得“灾难性”双重失败,因为测试 2 没有预期的设置(并且,更重要的是,如果您认为测试 2 依赖于测试 1 的通过,那么您需要担心测试 2 是否在测试 1 失败后通过)。

              这就是为什么您希望测试尽可能独立 - 文件内和文件间。

              依赖于不同文件中(组)测试之间的顺序是非常不明智的。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-10-06
                • 1970-01-01
                • 2010-10-30
                • 1970-01-01
                • 2013-04-17
                • 2011-09-20
                • 2010-12-09
                • 2010-12-16
                相关资源
                最近更新 更多