## Why is this an issue?

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

## Resources

- Modern Best Practices for Testing in Java - Philipp Hauer
- JUnit 5 documentation - Parameterized tests
- Writing Parameterized Tests With JUnit 4
- TestNG documentation - Parameters