【问题标题】:How to build up test cases in junit?如何在junit中构建测试用例?
【发布时间】:2013-12-26 23:22:01
【问题描述】:

我来自 Perl 背景,我使用 Test::More 处理单元测试。使用该框架,我知道测试发生的顺序并且可以依赖它,我知道 JUnit 框架不鼓励这样做。我已经看到了几种解决此问题的方法,但我想了解正确/预期的做事方式。

在我的 Perl 单元测试中,我会构建测试,知道如果测试 #3 通过,我可以在进一步的测试中做出一些假设。我不太明白如何在 JUnit 世界中构建它,以便我可以使每个测试完全独立。

例如,假设我有一个从字符串中解析日期的类。方法包括:

  • 解析一个简单的日期 (YYYY-MM-DD)
  • 使用备用分隔符(YYYY_MM_DD 或 YYYY/MM/DD)解析简单日期
  • 解析带有月份名称字符串的日期 (YYYY-MON-DD)
  • 使用不同语言的字符串月份名称解析日期
  • 等等

我通常在编写代码时将尽可能多的外部可访问方法集中到尽可能少的核心方法中,并尽可能多地重用代码(我敢肯定,这是我们大多数人都会做的事情)。所以,假设我对第一种方法有 18 个不同的测试,其中 9 个预计会通过,9 个会抛出异常。对于第二种方法,我只有 3 个测试,每个测试带有可以工作的分隔符 ('_' & '/'),​​另一个带有不工作的分隔符 ('*'),预计会失败。我可以将自己限制在引入的新代码上,因为我已经知道代码可以正确处理标准边界条件和常见错误,因为前 18 个测试已经通过。

在 Perl 世界中,如果测试 #20 失败,我知道这可能与特定分隔符有关,并且不是一般的日期解析错误,因为所有这些测试都已通过。在 JUnit 世界中,测试以随机顺序运行,如果测试 #20 失败,我不知道是因为一般日期解析问题还是因为分隔符。我必须去看看其他哪些失败了,然后在那里做出一些假设。当然,这并不太难做到,但也许在更大、更复杂的课程中,会更难做到。

其他人如何处理建立一组测试?我应该将每个测试放在一个单独的类中并使用测试套件吗?这似乎很乏味。 (在有人建议我将第一个 18 个放在一个类中,将第二个 3 个放在另一个类中,并为这些分组使用测试套件之前,让我们假设所有 18 个早期测试也都建立在彼此之上)。

再说一次,我知道有一些方法可以解决这个问题(JUnit 4.11+ 中的 FixedMethodOrder 或 JUnit-HierarchicalContextRunner),但我想了解该范式的用途。

【问题讨论】:

  • 你不能假设测试的执行顺序。有些运行程序的运行顺序与其他运行程序不同。通常人们每个代码类使用一个测试类。例如,如果您有抛出异常的测试,则将它们标记为此类,它们将成功。 @Test(expected=IndexOutOfBoundsException.class)

标签: java unit-testing junit


【解决方案1】:

在 JUnit 世界中,测试以随机顺序运行,如果测试 #20 失败,我不知道是因为一般日期解析问题还是因为分隔符。我得去看看其他哪些失败了,然后在那里做一些假设。

是的,这是正确的。如果您的代码中的某些内容被破坏,那么多个测试可能会失败。这是一件好事。使用意图揭示测试方法名称,并可能使用 JUnit 断言中的可选字符串消息参数来解释测试失败的确切原因。

其他人如何处理建立一组测试?我是否应该将每个测试放在一个单独的类中并使用测试套件?

一般约定是每个源类一个测试类。根据您使用的构建工具,您可能需要也可能不需要使用测试套件。如果您使用 Ant,您可能需要将测试收集到测试套件中,但如果您使用 Maven,maven 的测试插件会为您找到所有测试类,因此您不需要套件。

我还想指出,您应该尽可能多地使用 Java 接口进行编码。如果您正在测试依赖于接口I 实现的类C,那么您应该在C 测试类中模拟您的I 实现,以便单独测试C。您的模拟 I 应该遵循界面应该执行的操作。这也降低了失败测试的数量。如果您真正的 I 实现中存在错误,那么只有您的 I 测试应该失败,C 测试仍然应该全部通过(因为您正在针对伪造但有效的 I 实现进行测试)

【讨论】:

    【解决方案2】:

    不要担心套房。你会知道什么时候需要它们。我只需要使用它们几次,我并没有完全相信它们的实用性……但我把决定权留给你。

    就您的问题而言,JUnit 测试的传统方式是既不知道也不依赖于测试的执行顺序;这可确保您的测试不依赖于运行顺序,如果是,则说明您的测试*和验证有问题。

    单元测试背后的主要核心概念是它们测试代码单元 - 就像单个函数一样简单。如果你试图一次测试五种不同的东西,你的测试太大了,应该被打破。如果您要测试的方法本质上是单一的,并且难以测试,则应将其重构并分解为不同的责任部分。

    执行更大流程的测试更适合集成式测试,它倾向于写成单元测试,但实际上不是单元测试。

    我没有遇到过这样的情况,如果我知道如果某个测试失败,我可以期待其他测试中的不同行为。我从没想过需要注意这样的事情,因为我在单元测试中唯一关心的是在给定特定输入的情况下, 代码单元的行为方式。

    让您的测试小而易懂;测试应该只对结果做出一个断言(或对结果状态的一般断言)。

    *:这并不是说它完全损坏,但这类测试应该尽快修复。

    【讨论】:

    • 嗯,这就是我的观点:尽可能地减少测试。这就是为什么我对第一种方法进行了 18 次测试,因为每个测试都测试一个假设或边缘情况或错误条件。因此,当我的测试只检查我是否可以使用“_”作为分隔符而不是“-”时,它失败了,我怎么知道这是因为分隔符代码有问题,而不是一般数字解析错误?显然,如果一切正常,我不在乎它们执行的顺序,但是当出现问题时,知道之前的测试成功给了我一个线索来寻找。
    • 也许我只是不需要太担心什么时候会失败,因为(如果我的工作做得正确的话)这种情况不会经常发生,然后我就能负担得起额外的时间来找出坏的地方,哪里。
    • 您会知道这是分隔符代码中的问题,因为理想情况下,这只是您正在测试的内容。如果您不只是测试那段代码,那么如上所述,您的测试范围太广了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-05
    • 2013-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多