¿Debería cada rama posible de un método tener un junit separado?

Esta es más una cuestión de diseño.

Suppose you have a method like this (as an example):

if (x == 5) {
  c = 1;
} else {
 if (z != 2) {
  b = 6;
} else {
   a = 3;

Do you think it's best practice to have a junit for each possible branch? Ie, testx5, test xnot5znot2, testxnot5z2, etc, or something like:

void testMethod() {
// x is 5
test/assert code;

// x not 5, z not 2
test/assert code;

// x not 5, z is 2
test/assert code

// etc

EDIT: Just to be clear, my goal is complete code coverage. I just want to know opinions on whether I should make a new test for each branch or combine them in one test. Thank you for your input.

preguntado el 08 de noviembre de 11 a las 14:11

4 Respuestas

What you're discussing is called Cobertura de sucursales.

The conventional wisdom is if it's important enough to write code to cover that use case, it's important enough to write a test case to cover that code. So this would seem to say that 100% branch coverage is an excellent goal (and will also imply 100% statement coverage, but not necessarily 100% loop or 100% condition coverage).

However, you also need to balance the effort of writing tests with the value of getting those tests. For example, if the code you're testing has a try/catch to catch a checked exception, but the exception is almost never thrown (or difficult to cause to be thrown in a test case), then writing a test to cover that exception is probably not worth your time.

This is why you see in a lot of places that aiming for a certain % of test coverage is a bad idea, because you end up writing test cases to get coverage, not to find bugs. In your example, yes, each branch deserves it's own test case. In every piece of production code, it's probably not necessary.

respondido 08 nov., 11:18

La Preguntas frecuentes de JUnit seems to indicate that it is better to have more tests with fewer assertions, if only because JUnit will only report the first assertion failure in a test method. With one method, if you broke the x = 5 case, you'd have no way to tell if any of the x != 5 cases were still working.

respondido 08 nov., 11:19

Accepted for understanding the central issue I was wondering about and for the link to the JUnit FAQ. - Artista del hambre

In unit testing, your goal is to test behaviors -- not the "code". Think of the method you're testing a black box and you want to see if it works correctly. You don't know how it does it's job internally, but you expect certain results for certain inputs. So you'd want to create tests for different cases of how you'd expect the code to work as if you didn't know anything about how the internals of the method actually does it's job. So you'd write tests like "applysDiscountToShoppingCart" and "addsDeliveryFeeToShoppingCart".

Now, all that being said, it's also useful to create "edge cases" in which you're testing things that are likely to break (like nulls, zeros, negatives, data too big/small, etc) to see if it fails in an expected manner too. Usually to write those, you need to know how the method actually works. If you can design tests that will cover all your code, that's great! 100% test coverage is a definite thing to strive for, but it's not always practical (or useful) depending on the situation.

respondido 08 nov., 11:18

He's asking whether he should write separate test methods for different inputs/use cases of one method, or just place multiple assertions in one test method. This doesn't really answer that question, even though it is good information on its own. - G_H

That's a good point, I think I just read his question a bit differently. Ultimately, branches usually dictate different behaviors for different conditions, so the answer would still be multiple test methods with testing one behavior per test method. - Todd

That would seem to be the conventional wisdom. But I've also had cases where I preferred putting multiple assertions in one test method because otherwise the test class would get bloated for little gain. Like when testing hashCode or equals methods. You'd typically always test the same things, like transitivity and reflexivity, so I don't really care if I'm only seeing the first failure. One wrong assertion means the method is wrong, so I'd fix that before the rest which might depend on that assertion anyway. Writing tests is a hassle, it's understandable someone wants to make it quick. - G_H

Especially on build servers it is easier to have many different testcases/functions because it will be easy to identify which test fails. Another downside is that the testcase will halt if the first one fails, and you will not know the result of the other testcases.

For me personally this benefit stops when you have to do a lot of copy pasting to set up/explain the testcase, in that case I will just do several asserts in the same test case.

respondido 08 nov., 11:19

If you're doing lots of copy-pasta for your setup, shouldn't you be using a setUp() method for your test class? Not to mention, if the first assert fails, then you don't know if the other asserts Además, have problems. - Kane

I'm not talking about the setUp() function in jUnit, instead I talk about the part where the test case describes the situation that is tested (creates mocks etc). I will update the answer to reflect your good point that you won't see if the other asserts fail or not. - Thirler

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.