2010-04-26 56 views
8

我测试字符串乘法器类与multiply()方法,该方法2倍的数字为输入(如String)并返回结果数(如String基本的JUnit问题

public String multiply(String num1, String num2);  

我也做了实施和创建的测试类与涉及该输入字符串参数作为

  1. 有效号码以下测试用例
  2. 字符
  3. 特殊符号
  4. 空字符串
  5. Null值
  6. 负数
  7. 浮动
  8. 边界值
  9. 数字是有效的,但他们的产品是超出范围
  10. 号将+号(+23)

现在我的问题是:

  1. 我想知道,如果“每一个”的assertEquals()应该是在它自己的测试方法?或者,我可以将类似的测试用例(如testInvalidArguments())分组以包含涉及无效字符的所有断言,因为它们都引发相同的NumberFormatException?

  2. 如果测试像字符(“a”)这样的输入值,是否需要为所有场景包含测试用例? “一”作为第一个参数 “一”作为第二个参数 “A”和“B”为2个参数

  3. 按我的理解,这些单元测试的好处是找出案件来自用户的输入可能失败并导致异常。然后,我们可以给用户一个有意义的消息(要求他们提供有效的输入)而不是例外。这是正确的吗?而且,这是唯一的好处吗?

  4. 上述11个测试用例是否足够?我错过了什么?我过度了吗?什么时候够了?

  5. 从上述观点来看,我是否成功测试了multiply()方法?

回答

4

1)在测试的粒度(因此容易诊断)和单元测试代码的冗长之间有一个折衷。我个人很高兴能够采用相对粗糙的测试方法,特别是一旦测试和测试代码稳定下来。粒度问题仅在测试失败时才适用。 (如果我在多断言测试用例中出现故障,我要么修复第一个故障并重复,要么根据需要暂时破解测试用例,以确定发生了什么。)

2)使用您的常识。根据您对编写代码的理解,设计您的测试以锻炼所有质量不同的子数据库。认识到不可能来测试所有可能的输入,除了最微不足道的情况。

3)单元测试的目的是提供一定程度的保证,使被测试的方法能够做到他们需要做的事情。这意味着什么取决于正在测试的代码。例如,如果我是单元测试sort方法,验证用户输入是无关紧要的。

4)覆盖范围似乎是合理的。但是,如果没有详细说明您的班级需要做什么以及如何检查实际的单元测试,则不可能说您是否覆盖了所有内容。例如,你的方法是否应该处理前/后空格字符,带小数点的数字,诸如“123,456”的数字,使用非拉丁数字表示的数字,基数为42的数字?

5)定义“成功测试”。如果你的意思是,我的测试证明代码没有错误,那么答案是肯定的“否”。除非单元测试列举了每个可能的输入,否则它们不能构成正确性的证明。 (并且在某些情况下,甚至没有测试所有输入就足够了。)

除了最微不足道的情况外,测试无法证明缺少错误。它唯一可以证明的就是存在错误。如果你需要证明一个程序没有错误,你需要采用“正式方法”。即将正式的定理证明技术应用于您的程序。

而且,正如另一个答案所指出的那样,您需要将它提供给真正的用户,以查看他们可能会以意外输入的方式提出的问题。换句话说......所述或推断的用户需求是否实际上是完整且有效的。

5

1)我认为这是一个好主意,限制你在每个测试中断言的数量。 JUnit仅报告测试中的第一个失败,因此如果您有多个断言,可能会掩盖某些问题。能够看到通过的所有内容以及所有失败的内容会更有用。如果你在一次测试中有10 assertEquals,并且第一次失败,那么你只是不知道其他9会发生什么事。这些在调试时是很好的数据点。

2)是的,你应该包括所有输入的测试。

3)这不仅仅是需要测试的最终用户输入。你会想为任何可能失败的公共方法编写测试。有一些很好的指导方针,特别是关于吸气剂和吸附剂的,在JUnit FAQ

4)我认为你已经覆盖了很好。 (至少我不能想到别的,但请参阅#5)。

5)给它一些用户测试。他们总能找到我从来不想测试的样本数据。 :)

+0

@Bill - 这是乘法。除非执行操作有些奇怪,否则除以零除非相关。 – 2010-04-26 02:42:09

+0

@Stephen C:哦,对。我在那里得到了一点点偏离。谢谢,我编辑了我的回复。 – 2010-04-26 12:12:30

2

1)最好让你的测试小而专注。这样,当测试失败时,很明显测试失败的原因。这通常会导致每个测试只有一个断言,但并非总是如此。

然而,代替手工编码的测试为每个单独的“无效的情况下”,你可能想看看JUnit 4.4的理论(见JUnit 4.4 release notesthis blog post),或JUnit的Parameterized测试运行。

参数化测试和理论非常适合像这样的“计算”方法。另外,为了保持组织性,我可以做两个测试类,一个用于“好”输入,另一个用于“坏”输入。 2)你只需要包含你认为最有可能暴露代码中的任何错误的测试用例,而不是所有输入的所有可能组合(这在WizardOfOdds的评论中指出这是不可能的)。你提出的三套是好的,但我可能不会超过这三套。然而,使用理论或参数化测试可以让您添加更多场景。

3)编写单元测试有很多好处,而不仅仅是你提到的那个。其他一些好处包括:

  • 信心在你的代码 - 你有确定性高的法令,你的代码是正确的。
  • 对重构的信心 - 你可以重构你的代码,并知道如果你破坏了某些东西,你的测试会告诉你。
  • 回归 - 您将立即知道系统某个部分的更改是否无意中破坏了此特定方法。
  • 完整性 - 测试迫使您考虑您的方法可能接受的可能投入以及该方法应如何回应。

5)这听起来像你做了很好的工作,想出可能的测试场景。我认为你有所有重要的。

+0

+1 - 点#3的好东西。 – 2010-04-26 01:20:55

+0

@Jim Hurne:这不是因为单元测试通过了*“你知道它有效”* [原文如此]。当一个单元测试通过时,你知道*“它不起作用”*,这是非常**不同的。这是人们在单元测试中犯的最常见的错误之一:他们非常自信他们的代码能够工作......(顺便说一句,我有很多单行测试的代码行,所以不要误解我的观点单元测试) – SyntaxT3rr0r 2010-04-26 02:53:12

+0

@Jim Hurne:以同样的方式,你绝不能*保证*捕捉每一次回归。肯定有很多项目(以及很多非常引人注目的项目,这些项目都很好地进行了单元测试),其中出现的回归错误令人遗憾地未被任何单元测试所捕获。但是,当一个测试失败时,你知道你刚刚有一个回归。但是没有任何保证**,你会马上赶上你的回归。 – SyntaxT3rr0r 2010-04-26 02:55:43

2

真正的测试数量当然是无限的。这是不实际的。你必须选择有效的代表性案例。你似乎已经做到了。做得好。

6

单元测试是伟大的(在200 KLOC项目我的工作我已经得到尽可能多的单元测试代码常规代码),但(假设正确的单元测试):

  • 单元测试即通过不保证您的代码工作的它

这样的想法:

  • 一个失败的单元测试证明你的代码被打破

意识到这一点是非常重要的。

除此之外:

  • 通常不可能来测试所有可能的输入

然后,当你重构:

  • 如果所有你的单元测试是否通过不是意思是你没有引入一个回归

但是:

  • 如果你的单元测试的一个失败,你知道你已经引入了回归

这是真正的基础,应该是单元测试101

+0

+1 Stephen C和fastcodejava – SyntaxT3rr0r 2010-04-26 03:08:44

+0

+1对你也是如此。 – fastcodejava 2010-04-26 05:06:51

1

我只是想补充说,单元测试,如果你首先考虑可能的情况,你可以获得更多d之后以测试驱动开发方式实现,因为这将帮助您保持对当前案例的支持,这将使您能够以DRY方式创建最简单的实现。您也可能会使用一些测试覆盖工具,例如在Eclipse EclEmma中,它非常易于使用,并会告诉你测试是否已经执行了所有的代码,这可能有助于确定何时足够用(尽管这不是一个证明,只是一个指标)。通常,当谈到单元测试时,我非常喜欢Kent Becks的示例测试驱动开发书,我强烈推荐它。