You should think that in your calculator example you described 4 tests. Reasonable to think so, right? Even Junit will say that. But you only had one test.
Writing test cases
Every test case should be composed of 3 basic parts:
- data being tested
- operations to be carried out on such data
- measurements of expected results
I usually sum it up like this:
- dice
- operations
- measurements
The same data set undergoing the same operations must undergo all possible measurements within it @Test
.
It is also worth mentioning that each test case should be basically a unit independent of the others noted with @Test
. At most dependent on @Before
or whose side effects are undone in @After
. This means that each test case should be done in a totally isolated way from the others, so that they are not temporally coupled, where the execution of one precedes that of the other.
Modeling Your Test Case
In your case, we have as data:
- the properly constructed calculator
- the expression
"3/0"
The operation is:
- get the
Resultado
of the call for calculadora.calcular(expressao)
And the measurements:
error message in result:
String msgDeErro = r.getMensagem();
assertEquals(msgDeErro, "Impossível Dividir por Zero!");
resulting null number:
BigDecimal num = r.getNumResultante();
assertNull(num);
Thus, your 4 test methods are reduced to just one test:
@Test
public void dividindoPorZero() {
// dados
Calculadora calc = new Calculadora();
String expr = "3/0";
// operações
Resultado r = calc.calcular(expr);
// aferições
String msgDeErro = r.getMensagem(); // isso já verifica se 'r' é nulo, lançando NPE caso seja
assertEquals(msgDeErro, "Impossível Dividir por Zero!");
assertNull(r.getNumResultante)(
}
Much more clean, don’t you think? Not to mention more elegant too, taking into account the modeling.
Modeling exceptions
There are situations in which the code must make an exception. And that’s it. Whoever modeled the program did it to make an exception during an operation, for some reason (whether done right or wrong at concept or performance level are another five hundred, but if to work must be guaranteed the exception, then the situation that fires it needs to be tested).
For example, the Java language provides for the release of NullPointerException
in several cases. Among them, call a null object method. How can this be ascertained? Creating expectations!
For example, I can have these two test cases, one to cast exception and the other to not cast exception:
- Makes no exception:
- given: string with value
"abc"
- operation: call the method
.toString()
- measurement: arrived at the end
- Exception
- given: null string
- operation: call the method
.toString()
- measurement: launched the specific exception
NullPointerException
@Test
public void naoLancaExcecao() {
// dado
String a = "abc";
// operação
a.toString();
// aferição implícita, precisa não lançar exceção
}
@Test(expect = NullPointerException.class)
public void lancaExcecao() {
// dado
String a = null;
// operação
a.toString();
// aferição implícita, precisa lançar exceção do tipo NullPointerException
}
If you create an expectation that is not met, Junit marks it as an error. If you don’t create expectations and explode an exception, Junit also marks as an error.
Conditional execution
I believe you believed you were in this problem. But no, you were not. In this case, you simply don’t do unnecessary operations/measurements.
Recently where I work I needed to implement 36 test cases: 36 distinct data sets (4 variables: 3 states, 3 states, boolean, boolean) and 1 possible operation for each of these data sets.
The class in question had two methods: one that stated whether the main method should be executed and the main method. Obviously, if the main method should not be executed, I did not execute it. And the execution of the main method had 3 possible behaviors: launches exception A, launches exception B or runs clean.
I created an auxiliary method to create the dataset (it was very simple, simple enough that I didn’t need to create a test case to test my test utility). I will illustrate here 3 possible test cases (variables that are not displayed creation were created in @Before
):
@Test
public void naoExecuta() {
Dados d1 = criaDados(Enum.Tipo1, Enum.tipo1, true, true);
assertFalse(myObj.estahAtivo(d1));
}
@Test(expect = MyExceptionB.class)
public void cabum() {
Dados d1 = criaDados(Enum.Tipo1, Enum.tipo3, true, true);
assertTrue(myObj.estahAtivo(d1));
myObj.metodoExplosivo(d1);
}
@Test
public void naoExolode() {
Dados d1 = criaDados(Enum.Tipo1, Enum.tipo3, true, false);
assertTrue(myObj.estahAtivo(d1));
myObj.metodoExplosivo(d1);
}
It seems to me that you’re not using Junit the way it was meant to work
– Jefferson Quesado