我来自Perl背景,我使用Test::More
来处理单元测试。使用该框架,我知道测试发生的顺序并且可以依赖于该框架,我理解不会使用JUnit框架。我已经看到了几种解决这个问题的方法,但我想了解正确/预期的做事方式。
在我的Perl单元测试中,我会建立测试,知道如果测试#3通过,我可以在进一步测试中做出一些假设。我不太清楚如何在JUnit世界中构建它,以便我可以使每个测试完全独立。
例如,假设我有一个从字符串中解析日期的类。方法包括:
我通常编写代码,将尽可能多的外部可访问方法集中到尽可能少的核心方法中,尽可能多地重用代码(这是我们大多数人都会做的,我敢肯定)。所以,假设我对第一种方法有18种不同的测试,9种预期通过,9种抛出异常。对于第二种方法,我只有3个测试,每个测试都有一个工作的分隔符('_'和'/'),另一个有一个不起作用的分隔符('*'),预计会失败。我可以将自己限制在引入的新代码中,因为我已经知道代码正确处理标准边界条件和常见错误,因为前18个测试已经通过。
在Perl世界中,如果测试#20失败,我知道它可能与特定分隔符有关,并且不是一般日期解析错误,因为所有这些测试都已经通过。在JUnit世界中,测试以随机顺序运行,如果测试#20失败,我不知道是因为一般日期解析问题还是因为分隔符。我必须去看看其他哪些失败然后在那里做出一些假设。当然,这并不难做,但也许在一个更大,更复杂的课程中,这样做会更难。
其他人如何处理构建一组测试?我应该将每个测试放在一个单独的类中并使用测试套件吗?这看起来很单调乏味。 (并且在有人建议我将第一个18放在一个类中,第二个放在另一个类中,然后使用测试套件进行这些分组时,让我们假设所有18个早期测试都是相互建立的。)
而且,我知道有很多方法可以解决这个问题(JUnit 4.11+或JUnit-HierarchicalContextRunner中的FixedMethodOrder)但是我想要理解它的用途。
答案 0 :(得分:2)
在JUnit世界中,测试以随机顺序运行,如果测试#20失败,我不知道是因为一般日期解析问题还是因为分隔符。我必须去看看其他哪些失败然后在那里做出一些假设。
是的,这是正确的。如果代码中的某些内容被破坏,那么多个测试可能会失败。这是一件好事。使用intent揭示测试方法名称,并可能在JUnit断言中使用可选的String message参数来解释测试的确切失败。
其他人如何处理构建一组测试?我应该将每个测试放在一个单独的类中并使用测试套件吗?
一般约定是每个源类一个测试类。根据您使用的构建工具,您可能需要也可能不需要使用测试套件。如果您使用Ant,您可能需要将测试收集到测试套件中,但如果您使用Maven,maven的测试插件将为您找到所有测试类,因此您不需要套件。
我还想指出你应该尽可能地编写Java接口。如果您正在测试依赖于接口C
的实现的类I
,那么您应该在I
测试类中模拟C
实现,以便C
是孤立地测试的。你的模拟I
应该遵循界面假设来做。这也可以减少失败测试的数量。如果您的真实I
实施中存在错误,那么只有您的I
测试失败,C
测试仍应全部通过(因为您正在测试它是否违反假的但正在进行{ {1}}实施)
答案 1 :(得分:1)
不要担心套房。你会知道什么时候需要它们。我只需要使用它们几次,而且我并没有完全卖掉它们的用处......但我把这个决定留给你了。
对于你的问题而言 - 使用JUnit测试的传统方法既不知道也不依赖于测试的执行顺序;这可以确保您的测试不依赖于运行顺序,如果是,则测试*和验证有问题。
单元测试背后的主要核心概念是测试代码单元 - 就像单个函数一样简单。如果你试图同时测试五种不同的东西,你的测试太大了,应该被打破。如果您正在测试的方法本质上是单一的,并且难以测试,那么它应该被重构并分解为不同的责任部分。
执行更大流程的测试更适合于集成式测试,倾向被编写为单元测试,但实际单元测试。< / p>
我没有遇到这样的情况:如果我知道如果某个测试失败了,我可以期待其他测试中的不同行为。我从来没有想过要注意这样的事情,因为我在单元测试中唯一关心的是 代码单元在给定某个输入时的行为。
让您的测试小而易懂;测试应该只对结果做一个断言(或结果状态的一般断言)。
*:这并不是说它完全被打破,但是那些类型的测试应该尽快修复。