我正在使用 multiply()
方法测试一个 String 乘法器类,该方法将 2 个数字作为输入(作为 String
)并返回结果数字(作为 String
) code>)
public String multiply(String num1, String num2);
我已经完成了实现并创建了一个测试类,其中包含以下测试用例,涉及输入 String 参数作为
- 有效数字
- 字符
- 特殊符号
- 空字符串
- 空值
- 0
- 负数
- 浮点数
- 边界值
- 有效但其乘积超出范围的数字
- 数字将+符号(+23)
现在我的问题是:
-
我想知道“每个”assertEquals()是否应该在它自己的测试方法中?或者,我可以将类似的测试用例(例如 testInvalidArguments() )分组为包含涉及无效字符的所有断言,因为它们都抛出相同的 NumberFormatException 吗?
-
如果测试像字符(“a”)这样的输入值,我是否需要包含所有场景的测试用例?
“a”作为第一个参数
“a”作为第二个参数
“a”和“b”作为 2 个参数
根据我的理解,这些单元测试的好处是找出用户输入可能失败并导致异常的情况。然后,我们可以向用户提供有意义的消息(要求他们提供有效的输入)而不是异常。这是正确的吗?而且,这是唯一的好处吗?
-
上面提到的11个测试用例是否足够?我错过了什么吗?我是不是做得太过分了?什么时候够?
-
从上面的观点来看,我是否成功测试了multiply()方法?
I was testing a String multiplier class with a multiply()
method that takes 2 numbers as inputs (as String
) and returns the result number (as String
)
public String multiply(String num1, String num2);
I have done the implementation and created a test class with the following test cases involving the input String parameter as
- valid numbers
- characters
- special symbol
- empty string
- Null value
- 0
- Negative number
- float
- Boundary values
- Numbers that are valid but their product is out of range
- numbers will + sign (+23)
Now my questions are these:
-
I'd like to know if "each and every" assertEquals() should be in it's own test method? Or, can I group similar test cases like testInvalidArguments() to contains all asserts involving invalid characters since ALL of them throw the same NumberFormatException ?
-
If testing an input value like character ("a"), do I need to include test cases for ALL scenarios?
"a" as the first argument
"a" as the second argument
"a" and "b" as the 2 arguments
-
As per my understanding, the benefit of these unit tests is to find out the cases where the input from a user might fail and result in an exception. And, then we can give the user with a meaningful message (asking them to provide valid input) instead of an exception. Is that the correct? And, is it the only benefit?
-
Are the 11 test cases mentioned above sufficient? Did I miss something? Did I overdo? When is enough?
-
Following from the above point, have I successfully tested the multiply() method?
发布评论
评论(6)
单元测试很棒(在我正在工作的 200 KLOC 项目中,我拥有与常规代码一样多的单元测试代码),但是(假设单元测试正确):
可以这样想:
认识到这一点非常重要。
除此之外:
然后,当您重构时:
但是:
这确实是基础,应该是单元测试101。
Unit testing is great (in the 200 KLOC project I'm working I've got as many unit test code as regular code) but (assuming a correct unit test):
Think of it this way:
It is really important to realize this.
In addition to that:
And then, when you're refactoring:
But:
This is really fundamental and should be unit testing 101.
1)我确实认为限制每次测试中做出的断言数量是个好主意。 JUnit 仅报告测试中的第一次失败,因此如果您有多个断言,则某些问题可能会被掩盖。能够看到所有通过的和失败的事情会更有用。如果您在一个测试中有 10 个
assertEquals
,而第一个失败,那么您只是不知道其他 9 个会发生什么。在调试时这些将是很好的数据点。2)是的,您应该对所有输入进行测试。
3) 需要测试的不仅仅是最终用户的输入。您需要为任何可能失败的公共方法编写测试。在 JUnit 常见问题解答。
4)我认为你已经把它讲得很好了。 (至少我想不出其他的,但请参见#5)。
5)给一些用户测试一下。他们总是找到我从未想过要测试的样本数据。 :)
1) I do think it's a good idea to limit the number of assertions you make in each test. JUnit only reports the first failure in a test, so if you have multiple assertions some problems may be masked. It's more useful to be able to see everything that passed and everything that failed. If you have 10
assertEquals
in one test and the first one fails, then you just don't know what would have happened with the other 9. Those would be good data points to have when debugging.2) Yes, you should include tests for all of your inputs.
3) It's not just end-user input that needs to be tested. You'll want to write tests for any public methods that could possibly fail. There are some good guidelines for this, particularly concerning getters and setters, at the JUnit FAQ.
4) I think you've got it pretty well covered. (At least I can't think of anything else, but see #5).
5) Give it to some users to test out. They always find sample data that I never think of testing. :)
1) 测试的粒度(因此易于诊断)和单元测试代码的冗长程度之间需要权衡。我个人很乐意采用相对粗粒度的测试方法,尤其是在测试和测试代码稳定下来之后。粒度问题仅在测试失败时才相关。 (如果我在多断言测试用例中失败,我要么修复第一个失败并重复,要么根据需要暂时破解测试用例以弄清楚发生了什么。)
2)使用常识。根据您对代码编写方式的理解,设计测试以执行所有性质不同的子情况。认识到除了最微不足道的情况之外,不可能在所有情况下测试所有可能的输入。
3) 单元测试的目的是提供一定程度的保证,确保被测方法能够完成其要求的任务。这意味着什么取决于正在测试的代码。例如,如果我对
sort
方法进行单元测试,则用户输入的验证是无关紧要的。4)覆盖范围似乎合理。然而,如果没有详细说明您的班级需要做什么,也没有检查实际的单元测试,就不可能说您是否已经涵盖了所有内容。例如,您的方法是否应该处理前导/尾随空白字符、带小数点的数字、“123,456”等数字、使用非拉丁数字表示的数字、以 42 为基数的数字?
5) 定义“成功测试”。如果你的意思是,我的测试证明代码没有错误,那么答案肯定是“否”。除非单元测试枚举每一个可能的输入,否则它们不能构成正确性的证明。 (在某些情况下,甚至测试所有输入都不够。)
除了最微不足道的情况外,在所有情况下,测试都不能证明不存在错误。它唯一可以证明的是错误的存在。如果你需要证明一个程序没有bug,你需要诉诸“形式化方法”;即,将形式定理证明技术应用到您的程序中。
而且,正如另一个答案指出的那样,您需要将其提供给真实用户,看看他们可能会以意想不到的输入方式想出什么。换句话说……所陈述或推断的用户需求是否实际上完整且有效。
1) There is a tradeoff between granularity of tests (and hence ease of diagnosis) and verbosity of your unit test code. I'm personally happy to go for relatively coarse-grained test methods, especially once the tests and tested code have stabilized. The granularity issue is only relevant when tests fail. (If I get a failure in a multi-assertion testcase, I either fix the first failure and repeat, or I temporarily hack the testcase as required to figure out what is going on.)
2) Use your common sense. Based on your understanding of how the code is written, design your tests to exercise all of the qualitatively different subcases. Recognize that it is impossible to test all possible inputs in all but the most trivial cases.
3) The point of unit testing is to provide a level of assurance that the methods under test do what they are required to do. What this means depends on the code being tested. For example, if I am unit testing a
sort
method, validation of user input is irrelevant.4) The coverage seems reasonable. However, without a detailed specification of what your class is required to do, and examination of the actual unit tests, it is impossible to say if you ave covered everything. For example, is your method supposed to cope with leading / trailing whitespace characters, numbers with decimal points, numbers like "123,456", numbers expressed using non-latin digits, numbers in base 42?
5) Define "successfully tested". If you mean, do my tests prove that the code has no errors, then the answer is a definite "NO". Unless the unit tests enumerate each and every possible input, they cannot constitute a proof of correctness. (And in some circumstances, not even testing all inputs is sufficient.)
In all but the most trivial cases, testing cannot prove the absence of bugs. The only thing it can prove is that bugs are present. If you need to prove that a program has no bugs, you need to resort to "formal methods"; i.e. applying formal theorem proving techniques to your program.
And, as another answer points out, you need to give it to real users to see what they might come up with in the way of unexpected input. In other words ... whether the stated or inferred user requirements are actually complete and valid.
当然,真正的测试次数是无限的。那是不切实际的。你必须选择有效的代表性案例。你似乎已经这么做了。好工作。
True numbers of tests are, of course, infinite. That is not practical. You have to choose valid representative cases. You seem to have done that. Good job.
1)最好保持你的测试小而集中。这样,当测试失败时,就可以清楚测试失败的原因。这通常会导致每个测试产生一个断言,但并非总是如此。
但是,您可能需要查看 JUnit 4.4 理论(请参阅 JUnit 4.4 发行说明 和 这篇博文),或 JUnit 参数化测试运行程序。
参数化测试和理论非常适合像这样的“计算”方法。此外,为了保持事情井井有条,我可能会创建两个测试类,一个用于“好”输入,一个用于“坏”输入。
2)您只需要包含您认为最有可能暴露代码中任何错误的测试用例,而不是所有输入的所有可能组合(正如 WizardOfOdds 在他的评论中指出的那样,这是不可能的)。您提出的三组都很好,但我可能不会测试比这三组更多的组。然而,使用理论或参数化测试可以让您添加更多场景。
3)编写单元测试有很多好处,不仅仅是你提到的那个。其他一些好处包括:
5)听起来你在提出可能的测试场景方面做得很好。我想你已经掌握了所有重要的内容。
1) It's best to keep your tests small and focused. That way, when a test fails, it's clear why the test failed. This usually results in a single assertion per test, but not always.
However, instead of hand-coding a test for each individual "invalid scenario", you might want to take a look at JUnit 4.4 Theories (see the JUnit 4.4 release notes and this blog post), or the JUnit Parameterized test runner.
Parametrized tests and Theories are perfect for "calculation" methods like this one. In addition, to keep things organized, I might make two test classes, one for "good" inputs, and one for "bad" inputs.
2) You only need to include the test cases that you think are most likely to expose any bugs in your code, not all possible combinations of all inputs (that would be impossible as WizardOfOdds points out in his comments). The three sets that you proposed are good ones, but I probably wouldn't test more than those three. Using theories or parametrized tests, however, would allow you to add even more scenarios.
3) There are many benefits to writing unit tests, not just the one you mention. Some other benefits include:
5) It sounds like you did a good job with coming up with possible test scenarios. I think you got all the important ones.
我只是想补充一点,通过单元测试,如果您首先考虑可能的情况,然后以测试驱动开发的方式实施,您可以获得更多,因为这将帮助您专注于当前的情况,这将使您可以以 DRY 方式创建最简单的实现。您可能还使用一些测试覆盖率工具,例如在 Eclipse EclEmma 中,它非常易于使用,并且会向您显示测试是否已执行所有代码,这可能会帮助您确定何时足够(尽管这不是证明,只是一个指标)。一般来说,当谈到单元测试时,我受到 Kent Becks 的《测试驱动开发示例》一书的很大启发,我强烈推荐它。
I just want to add, that with unit testing, you can gain even more if you think first of the possible cases and after that implement in the test driven development fashion, because this will help you stay focuesed on the current case and this will enable you to create easiest implementation possible in DRY fashion. You might also be usng some test coverage tool, e.g. in Eclipse EclEmma, which is really easy to use and will show you if tests have executed all of your code, which might help you to determine when it is enough (although this is not a proof, just a metric). Generally when it comes to unit testing I was much inspired by Kent Becks's Test Driven Development by Example book, I strongly recommend it.