This rule raises an issue when at least 3 test methods could be refactored into a single parameterized test with less than 4 parameters.

Why is this an issue?

When multiple tests differ only by a few hardcoded values, they should be refactored into a single parameterized test. This reduces duplication, makes the tests easier to read, and lowers the risk of introducing bugs when the test logic needs to change.

Parameterized tests are supported by most testing frameworks.

The right balance still needs to be found. There is little value in parameterizing tests when the resulting test becomes significantly more complex than the original versions.

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

Resources