单元测试:在设置方法中添加断言是一种好的实践吗?

38

在单元测试中,setup方法用于创建测试所需的对象。

在这些setup方法中,我喜欢使用断言:我知道我想在这些对象中看到什么值,并且我喜欢通过断言来记录这些信息。

在stackoverflow上最近关于单元测试调用其他单元测试的帖子中,普遍感觉是单元测试不应该调用其他测试: 那个问题的答案似乎是你应该重构你的setup,使得测试用例不相互依赖。

但是,在“带有断言的设置”和调用其他单元测试的单元测试中没有太大区别。

因此我的问题是:在setup方法中使用断言是好的做法吗?

编辑:

答案是:总体而言,这不是一个好的做法。如果需要测试setup结果,建议添加一个带有断言的单独的测试方法(我选择的答案);为了记录意图,考虑使用Java断言。


1
阅读有关编写良好单元测试的文章:http://blog.codeville.net/2009/08/24/writing-great-unit-tests-best-and-worst-practises/ - Kane
5个回答

20

与其在设置中使用断言检查结果,我使用了一个简单的测试(作为其他测试方法之一,但定位为第一个测试方法)。

我看到了几个优点:

  • 设置保持简短且重点突出,易于阅读。
  • 断言仅运行一次,更高效

用法和讨论

例如,我将该方法命名为testSetup()。

要使用它,在该类中有一些测试错误时,我知道如果testSetup()出现错误,我不需要处理其他错误,我需要先修复这个错误。

如果有人对此感到困扰,并想要明确这种依赖关系,则可以在setup()方法中调用testSetup()。但我认为这并不重要。我的观点是,在JUnit中,您已经可以在其他测试中拥有类似的东西:

  1. 一些测试用于测试本地代码,
  2. 一些测试用于调用更全局的代码,间接调用与前一个测试相同的代码。

当您阅读两个测试均失败的测试结果时,您已经必须处理此依赖关系,该依赖关系不在测试中,而在所调用的代码中。您必须先修复简单测试,然后重新运行全局测试以查看它是否仍然失败。 这就是为什么我不会被我之前解释的隐式依赖所困扰的原因。


2
今天早上我和同事讨论了这个问题,我们也想出了这个解决方案。很高兴听到你对它有积极的经验! - avandeursen
@Kaka,谢谢你热情的评论以及反馈所提供的支持。 :-) - KLE
@KLE:这是一种有趣且高效的策略。但我想知道你如何确保在使用过滤器(即 --gtest_filter)执行测试时会执行 testSetup() - Sampath

11

它们是不同的场景,我看不出相似之处。

设置方法应该包含在夹具(fixture)中所有测试中通用的代码。因此,在测试代码的其余部分执行前必须确保某些条件满足,将assert放在测试设置(setup)方法中没有本质上的错误。设置(setup)是测试的一部分, 如果assert失败,人们将会发现哪个前提条件失败了。

另一方面,如果要断言的设置(setup)足够复杂,说明可能存在问题。此外,如果并非所有测试都需要设置(setup)的全部输出,则这表明夹具(fixture)的内聚性差,应该基于不同场景对其进行拆分和/或重构。

正是由于这个原因,我倾向于避免使用设置(setup)方法。在可能的情况下,我使用私有工厂方法或类似方法设置(setup),这使得测试更易读且避免了混淆。有时这是不可行的(例如,当涉及到紧密耦合的类和/或编写集成测试时),但对大多数测试来说,这样做就足够了。


11
在Setup/TearDown方法中使用断言是不可取的。如果用户需要“理解”一些测试逻辑不在测试方法中,那么这将使测试 less 可读性。 有时您没有选择,只能使用setup/teardown方法来处理它们原本不打算处理的内容。
这个问题中存在一个更大的问题:调用另一个测试的测试会导致某些测试问题的产生。 每个测试应该测试代码的特定方面,并且在其中应该只有一个或两个断言,因此如果您的测试调用另一个测试,则可能在该测试中测试了太多东西。 更多信息请阅读:单元测试:一个测试,一个断言-其原因

有趣的指针,涉及一个测试/一个断言,我喜欢你把它改成“一个或两个”的说法。 - avandeursen
1
URL已经失效,这是更新后的链接:单元测试:一个测试,一个断言 - 为什么它有效 - James

4

跟随内心/眨眼决定。在Setup方法中使用断言可以记录意图,提高可读性。所以,就我个人而言,我会支持您这样做。
这与测试调用其他测试不同-这是不好的。没有测试隔离。一个测试不应该影响另一个测试的结果。

虽然这不是一个经常使用的情况,但我有时会在Setup方法中使用Asserts,以便我知道测试设置是否按照我打算进行;通常当我处理那些我自己没有编写的组件时。在错误选项卡中读取“设置失败!” 的断言失败-可以快速帮助我专注于设置代码,而不必查看一堆失败的测试。

Setup失败通常应导致该夹具中的所有测试失败-这是您的鼻子应该很快察觉到的气味。“所有测试都失败通常意味着设置被破坏了。”因此,并非总是需要断言。也就是说,要实事求是,看看您的具体上下文并“根据口味添加”。


好的观点--谢谢。这意味着在两种不同的情况下使用JUnit断言:(1)记录意图;(2)检查结果是否符合预期。 - avandeursen

3

在必要的情况下,我使用Java断言而不是JUnit断言。例如,当您使用其他实用程序类来设置测试数据时。

byte[] pkt = pktFactory.makePacket(TIME, 12, "23, F2");
assert pkt.length == 15;

失败的含义是“系统甚至无法尝试运行此测试”。

我喜欢这个想法。这引出了一个普遍的问题,即混合使用Java断言和JUnit Assert方法是否是一种好的实践,但我喜欢你的推理。 - avandeursen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接