Do you know Parameterized Tests?
Refactoring production code is common behavior to make it more readable during software development. What about test codes, do you have the same care?
In this article, I wanna show you an easy way to reduce lines and be more produtive when writing tests.
The following examples use Junit5. In order for tests to work properly, it is necessary to install junit-jupiter-params.
I’ve created a simple code that performs a basic math operation.
public class Calculator {
private int x;
private int y;
private String operation;
public Calculator (int x, int y, String operation) {
this.x = x;
this.y = y;
this.operation = operation;
}
public int calculate() {
switch (this.operation) {
case "add":
return x + y;
case "subtract":
return x - y;
case "multiply":
return x * y;
case "divide":
return x / y;
default:
return 0;
}
}
}
The example has no secret. It receives x
, y
and, according to the operation, the result will be returned. If the operation is unknown the result will be zero.
Testing this code without parameterized tests would generate five methods at least and a lot of lines like these ones:
public class CalculatorTest {
@Test
void mustTestAddition() {
// given
Calculator calculator = new Calculator(10, 2, "add");
// when
int result = calculator.calculate();
// then
Assertions.assertEquals(result, 12);
}
@Test
void mustTestSubtraction() {
// given
Calculator calculator = new Calculator(10, 2, "subtract");
// when
int result = calculator.calculate();
// then
Assertions.assertEquals(result, 8);
}
@Test
void mustTestMultiplication() {
// given
Calculator calculator = new Calculator(10, 2, "multiply");
// when
int result = calculator.calculate();
// then
Assertions.assertEquals(result, 20);
}
@Test
void mustTestDivision() {
// given
Calculator calculator = new Calculator(10, 2, "divide");
// when
int result = calculator.calculate();
// then
Assertions.assertEquals(result, 5);
}
@Test
void mustTestInvalidOperation() {
// given
Calculator calculator = new Calculator(10, 2, "any");
// when
int result = calculator.calculate();
// then
Assertions.assertEquals(result, 0);
}
}
In summary, one test for each operation. Now, see how it works with parameterized tests.
public class CalculatorTest {
@ParameterizedTest
@CsvSource({
"10,2,add,12",
"10,2,subtract,8",
"10,2,multiply,20",
"10,2,divide,5",
"10,2,any,0"
})
void mustTestCalculatorOperations(int x, int y, String operation, int expectedResult) {
// given
Calculator calculator = new Calculator(x, y, operation);
// when
int result = calculator.calculate();
// then
Assertions.assertEquals(result, expectedResult);
}
}
Just add the @ParameterizedTest
annotation, define the scenarios inside @CsvSource
following the sequence of parameters on the test method and execute.
Although this text is simple, I think the goal was achieved. However, parameterized tests have quite options to explore. If you wanna know more about it, I do recommend the Guide to JUnit 5 Parameterized Tests.
Parameterized Tests
Java
Junit
Discussion and feedback