When multiple tests differ only by a few hardcoded values they should be refactored as a single "parameterized" test. This reduces the chances of
adding a bug and makes them more readable. Parameterized tests exist in most test frameworks (JUnit, TestNG, etc…​).
The right balance needs of course to be found. There is no point in factorizing test methods when the parameterized version is a lot more complex
than initial tests.
This rule raises an issue when at least 3 tests could be refactored as one parameterized test with less than 4 parameters. Only test methods which
have at least one duplicated statement are considered.
Noncompliant code example
with JUnit 5
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class AppTest
{
@Test
void test_not_null1() { // Noncompliant. The 3 following tests differ only by one hardcoded number.
setupTax();
assertNotNull(getTax(1));
}
@Test
void test_not_null2() {
setupTax();
assertNotNull(getTax(2));
}
@Test
void test_not_nul3l() {
setupTax();
assertNotNull(getTax(3));
}
@Test
void testLevel1() { // Noncompliant. The 3 following tests differ only by a few hardcoded numbers.
setLevel(1);
runGame();
assertEquals(playerHealth(), 100);
}
@Test
void testLevel2() { // Similar test
setLevel(2);
runGame();
assertEquals(playerHealth(), 200);
}
@Test
void testLevel3() { // Similar test
setLevel(3);
runGame();
assertEquals(playerHealth(), 300);
}
}
Compliant solution
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
public class AppTest
{
@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void test_not_null(int arg) {
setupTax();
assertNotNull(getTax(arg));
}
@ParameterizedTest
@CsvSource({
"1, 100",
"2, 200",
"3, 300",
})
void testLevels(int level, int health) {
setLevel(level);
runGame();
assertEquals(playerHealth(), health);
}
}