设置/拆卸会损害测试可维护性吗?

时间:2009-07-06 14:35:22

标签: unit-testing dry fixtures factory-pattern maintainability

这似乎引发了another question和我的一些对话 认为值得转入自己的问题。

DRY原则似乎是我们对抗维护的首选武器 问题,但是测试代码的维护呢?做同样的经验法则 应用

开发人员测试社区中的一些强烈的声音认为 设置和拆卸是有害的,应该避免......仅举几例:

事实上,出于这个原因,xUnit.net已经完全从框架中删除了它们 (虽然有ways to get around this self-imposed limitation)。

你的经历是什么?设置/拆卸是否会损坏或帮助测试可维护性?

UPDATE :像JUnit4或TestNG(@ BeforeClass,@ BeforeGroups等)中提供的更细粒度的构造会有所作为吗?

9 个答案:

答案 0 :(得分:9)

设置和拆卸方法的大多数(如果不是全部)有效用途可以编写为工厂方法,允许DRY而不会遇到似乎受设置/拆卸范例困扰的问题

如果您正在实施拆解,通常这意味着您不进行单元测试,而是进行集成测试。很多人用这个作为没有拆卸的理由,但是IMO应该有集成和单元测试。我个人会将它们分成不同的程序集,但我认为一个好的测试框架应该能够支持这两种类型的测试。并非所有良好的测试都将进行单元测试。

但是,通过设置,似乎有许多原因可以解释为什么在实际运行测试之前需要做的事情。例如,构造对象状态以准备测试(例如设置依赖注入框架)。这是设置的正当理由,但可以像工厂一样轻松完成。

此外,类和方法级别设置/拆卸之间存在区别。在考虑你要做的事情时,需要牢记这一点。

我使用setup / teardown范例时遇到的最大问题是我的测试并不总是遵循相同的模式。这使我开始使用工厂模式,这使我可以同时具有DRY,同时具有可读性,而不会让其他开发人员感到困惑。走工厂路线,我已经能够拿到我的蛋糕并吃掉了。

答案 1 :(得分:2)

他们确实帮助我们的测试可维护性。我们的“单元”测试实际上是完整的端到端集成测试,写入数据库并检查结果。不是我的错,当我到这里时他们就像那样,我正在努力改变一切。

无论如何,如果一个测试失败,它继续进行下一个测试,试图从数据库中的第一个测试进入同一个用户,违反唯一性约束,并且失败只是从那里级联。将用户创建/删除移动到[Fixture] [SetUp | TearDown]方法允许我们看到一个测试失败,没有一切变得混乱,让我的生活变得更轻松,更少刺激。

答案 2 :(得分:2)

我认为DRY原则适用于测试和代码一样多,但它的应用是不同的。在代码中,你需要更长的时间才能在代码的两个不同部分执行相同的操作。在测试中,需要这样做(做很多相同的设置)肯定是一种气味,但解决方案不一定要将重复分解为设置方法。它可能使状态更容易在类本身中设置或者隔离被测试的代码,因此它更少依赖于这个状态量而有意义。

鉴于每次测试只测试一件事的一般目标,在某些情况下(例如创建某种类型的对象),实际上不可能避免一遍又一遍地做同样的事情。如果你发现你有很多,那么可能值得重新考虑测试方法,例如引入参数化测试等。

我认为设置和拆卸应该主要用于建立环境(例如注入以使环境成为测试环境而不是生产环境),并且不应包含作为测试的一部分的步骤。

答案 3 :(得分:2)

我同意约瑟夫所说的一切,尤其是关于泪滴是编写集成测试的标志的部分(99%的时间是我用过的),但除此之外我会说使用设置是一个很好的指标,表明测试应该在逻辑上组合在一起,以及何时将测试分成多个测试类。

将测试应用于遗留代码时,我对大型设置方法没有任何问题,但设置应该是套件中的每个测试所共有的。当您发现自己的设置方法确实在进行多项设置时,就可以将测试分成多个案例了。

按照"Test Driven"中的示例,设置方法来自 删除测试用例中的重复。

答案 4 :(得分:1)

我经常在Java和Python中使用安装程序,经常设置协作者(实际或测试,取决于)。如果测试对象没有构造函数或只有协作者作为构造函数,我将创建对象。对于一个简单的价值类,我通常不会打扰它们。

我在Java中很少使用拆解。在Python中,它被更频繁地使用,因为我更有可能改变全局状态(特别是猴子修补模块以获得被测模块的用户)。在这种情况下,我想要一个拆解,如果测试失败,将保证被调用。

集成测试和功能测试(通常使用xunit框架)更可能需要设置和拆卸。

要记住的一点是要考虑fixtures,而不仅仅是DRY。

答案 5 :(得分:1)

我本身没有测试设置和拆卸方法的问题。

问题在于,如果您有测试设置和拆解方法,则意味着每个测试都会重复使用相同的测试对象。这是一个潜在的错误向量,就好像您忘记在测试之间清理某些状态元素一样,您的测试结果可能会依赖于顺序。我们真正想要的是不共享任何州的测试。

xUnit.Net摆脱了setup / teardown,因为它为每个运行的测试创建了一个新对象。本质上,构造函数成为设置方法,终结器成为拆解方法。测试之间没有(对象级)状态,消除了这个潜在的错误向量。

我编写的大多数测试都有一定的设置,即使它只是创建我需要的模拟并将测试的对象连接到模拟。他们不做的是在测试之间共享任何状态。拆解只是确保我不共享该状态。

答案 6 :(得分:0)

我没有时间阅读你发布的内容,但我特别喜欢这条评论:

  

每个测试都被迫对其运行所需的内容进行初始化。

设置和拆卸是方便的方法 - 它们不应该尝试做更多的事情,而不是使用其默认构造函数等初始化类。在五个测试类中需要三个测试需要的公共代码不应该出现在那里 - 每个三个测试应直接调用此代码。这也使得测试不会因为你改变了常见的初始化程序而踩到彼此的脚趾并打破了一堆测试。主要问题是在所有测试之前调用它 - 而不仅仅是特定的测试。大多数测试应该很简单,而更复杂的测试需要初始化代码,但是当您不必在设置中进行复杂的初始化并在拆除时进行复杂的破坏时,更容易看到简单测试的简单性。思考测试实际上应该完成什么。

答案 7 :(得分:0)

就个人而言,我发现设置和拆卸并不总是邪恶的,而且这种推理方式有点教条。但我称他们为没问题 code smell用于单元测试。我认为他们的使用应该是合理的,原因如下:

  1. 测试代码本质上是程序性的。通常,设置/拆卸执行会降低测试可读性/焦点。
  2. 设置方法倾向于初始化超过任何单个测试所需的内容。当被滥用时,他们会变得笨拙。对象母亲,测试数据构建器,也许像FactoryGirl这样的框架似乎更适合初始化测试数据。
  3. 他们鼓励“背景膨胀” - 测试环境变得越大,它的可维护性就越小。
  4. 如果我的设置/拆卸没有这样做,我认为他们的使用是有道理的。测试中总会有一些重复。 Neal Ford表示这是“测试可以湿润但不能浸泡......”此外,我认为当我们不是专门讨论单元测试时,它们的使用更合理,但是更广泛地进行集成测试。

    独立工作,这从来就不是一个问题。但我发现在团队环境中维护测试套件非常困难,而且往往是因为我们不会立即理解彼此的代码,或者不想通过它来理解它。从测试的角度来看,我发现在测试中允许一些重复可以减轻这种负担。

    我很想听听其他人对此的看法。

答案 8 :(得分:0)

如果您需要进行设置和拆卸以使您的单元测试工作,那么真正需要的是模拟对象?