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);
    }
}

See