This rule raises an issue when at least 3 test methods could be refactored into a single parameterized test with less than 4 parameters.
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.
Duplicated test methods lead to higher maintenance costs and increased likelihood of bugs. When test logic needs to be updated, developers must remember to make identical changes across multiple methods, which is easy to forget.
This can result in inconsistent test coverage where some variations of a test are updated while others remain outdated. Such inconsistencies can cause false positives or, worse, false negatives that let bugs slip through.
The impact on team productivity compounds over time as the test suite grows. More time is spent maintaining redundant code instead of writing new tests or improving application code.
Use pytest’s @pytest.mark.parametrize decorator to combine similar tests into a single parameterized test. The decorator accepts a
string of parameter names and a list of tuples containing the values for each test case.
import pytest
class TestApp:
def test_level1(self): # Noncompliant
set_level(1)
run_game()
assert player_health() == 100
def test_level2(self):
set_level(2)
run_game()
assert player_health() == 200
def test_level3(self):
set_level(3)
run_game()
assert player_health() == 300
import pytest
class TestApp:
@pytest.mark.parametrize("level,health", [
(1, 100),
(2, 200),
(3, 300),
])
def test_levels(self, level, health):
set_level(level)
run_game()
assert player_health() == health
For unittest, you can use the parameterized library to achieve similar functionality. Install it with pip install
parameterized and use the @parameterized.expand decorator.
import unittest
class TestApp(unittest.TestCase):
def test_not_null1(self): # Noncompliant
setup_tax()
self.assertIsNotNone(get_tax(1))
def test_not_null2(self):
setup_tax()
self.assertIsNotNone(get_tax(2))
def test_not_null3(self):
setup_tax()
self.assertIsNotNone(get_tax(3))
import unittest
from parameterized import parameterized
class TestApp(unittest.TestCase):
@parameterized.expand([1, 2, 3])
def test_not_null(self, tax_id):
setup_tax()
self.assertIsNotNone(get_tax(tax_id))