You should think that in your calculator example you described 4 tests. Reasonable to think so, right? Even JUnit will say that. But you did only 1 test.
Writing test cases
Every test case should consist of 3 basic parts:
Data being tested
operations to be performed on this data
expected results measurements
I usually summarize this way:
The same data set undergoing the same operations must undergo all possible measurements within the same @Test
.
It is also worth mentioning that each test case should basically be a unit independent of the others annotated with @Test
. At most dependent on @Before
or whose side effects are broken in @After
. This means that each test case should be done in a way totally isolated from the others, so that they are not temporally coupled, where the execution of one precedes 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 Resultado
of call from 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);
So 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 , do not you think? Not to mention that more elegant too, given the modeling.
Modeling Exceptions
There are situations where code should raise an exception. And ready. Whoever modeled the program did so to throw an exception during an operation, for some reason (whether it was done right or wrong at concept level or performance is another five hundred, but if to operate the exception must be guaranteed, then the situation that triggers it needs be tested).
For example, the Java language expects to launch NullPointerException
in a number of cases. Among them, call method of a null object. How can this be ascertained? Creating Expectations!
For example, I can have these two test cases, one for throwing an exception and the other for not throwing an exception:
No exception:
- given: string with the value
"abc"
- operation: call method
.toString()
- verification: arrived at the end
Exception throw
- given: string null
- operation: call method
.toString()
- gauging: has thrown 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 do not create expectations and explode an exception, JUnit also marks as an error.
Conditional execution
I believe you believe that you were in this problem. But no, you were not. In that case, you simply do not make the operations / measurements unnecessary.
Recently where I work I had to implement 36 test cases: 36 different 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 said whether the main method should be performed and the main method. Obviously, if the main method should not be run, I would not run it. And the main method execution had 3 possible behaviors: it throws exception A, throws exception B or executes clean.
I created a helper method to create the dataset (it was pretty simple, simple enough so I did not need to create a test case to test my test utility). Here are three possible test cases (variables that do not show 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);
}