基本的jUnit问题

时间:2010-04-26 00:35:12

标签: java unit-testing junit

我正在使用multiply()方法测试字符串乘数类,该方法将2个数字作为输入(作为String)并返回结果编号(作为String

public String multiply(String num1, String num2);      

我已完成实现并创建了一个测试类,其中包含以下测试用例,其中包含输入String参数

  1. 有效数字
  2. 字符
  3. 特殊符号
  4. 空字符串
  5. 空值
  6. 0
  7. 负数
  8. float
  9. 边界值
  10. 有效但产品超出范围的数字
  11. 数字将+符号(+23)
  12. 现在我的问题是:

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

    2. 如果测试输入值如字符(“a”),我是否需要包含所有方案的测试用例? “a”作为第一个论点 “a”作为第二个论点 “a”和“b”作为2个参数

    3. 根据我的理解,这些单元测试的好处是找出用户输入可能失败并导致异常的情况。然后,我们可以为用户提供有意义的消息(要求他们提供有效的输入)而不是异常。那是对的吗?而且,这是唯一的好处吗?

    4. 上面提到的11个测试用例是否足够?我错过了什么?我过头了吗?什么时候够了?

    5. 从以上几点开始,我是否成功测试了multiply()方法?

6 个答案:

答案 0 :(得分:6)

单元测试很棒(在我正在工作的200 KLOC项目中,我有与常规代码一样多的单元测试代码)但是(假设正确的单元测试):

  • 通过的单元测试保证您的代码正常运行

这样想:

  • 失败的单元测试证明您的代码已损坏

认识到这一点非常重要。

除此之外:

  • 通常无法测试所有可能的输入

然后,当你重构时:

  • 如果您的所有单元测试都通过意味着您没有引入回归

可是:

  • 如果您的某个单元测试失败,则表示您已经引入了回归

这是非常基础的,应该是单元测试101.

答案 1 :(得分:5)

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

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

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

4)我认为你已经很好地覆盖了它。 (至少我不能想到其他任何东西,但请看#5)。

5)将其提供给一些用户进行测试。他们总能找到我从未想过测试的样本数据。 :)

答案 2 :(得分:4)

1)在测试的粒度(因此易于诊断)和单元测试代码的详细程度之间存在权衡。我个人很高兴采用相对粗粒度的测试方法,特别是一旦测试和测试代码稳定下来。粒度问题仅在测试失败时才相关。 (如果我在多断言测试用例中出现故障,我要么修复第一个失败并重复,要么我根据需要暂时破解测试用例以弄清楚发生了什么。)

2)运用你的常识。根据您对代码编写方式的理解,设计测试以运用所有定性不同的子类。认识到除了最微不足道的情况之外,所有可能的输入都是不可能

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

4)报道似乎合理。但是,如果没有详细说明你的课程要求做什么,以及对实际单元测试的检查,就不可能说你是否已经涵盖了所有内容。例如,您的方法应该处理前导/尾随空格字符,带小数点的数字,数字如“123,456”,使用非拉丁数字表示的数字,基数42中的数字?

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

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

而且,正如另一个答案所指出的那样,你需要把它交给真正的用户,看看他们可能会以意外输入的方式提出什么。换句话说......所声明的或推断的用户要求是否实际完整且有效。

答案 3 :(得分:2)

1)最好让你的测试保持小而专注。这样,当测试失败时,很清楚测试失败的原因。这通常会导致每次测试单个断言,但并非总是如此。

但是,您可能希望查看JUnit 4.4 Theories(请参阅JUnit 4.4 release notesthis blog post)或JUnit,而不是为每个“无效方案”手动编写测试。 Parameterized测试运行员。

参数化测试和理论非常适合像这样的“计算”方法。另外,为了保持井井有条,我可以制作两个测试类,一个用于“好”输入,一个用于“坏”输入。

2)您只需要包含您认为最有可能暴露代码中任何错误的测试用例,而不是所有输入的所有可能组合(这在WizardOfOdds的评论中指出是不可能的)。你提出的三组是好的,但我可能不会测试超过这三组。但是,使用理论或参数化测试可以添加更多场景。

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

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

5)听起来你做了一个很好的工作,想出可能的测试场景。我认为你得到了所有重要的东西。

答案 4 :(得分:2)

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

答案 5 :(得分:1)

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