conditional-in-test (SMP803)¶
Flags if statements, match statements and conditional expressions in test functions.
Why is this bad?¶
A branching test runs a different check depending on its input or environment, so a passing run does not say which path was verified. With one parametrized case per path, pytest reports each path on its own.
Example¶
tests/test_paths.py
1import sys
2
3
4def test_separator() -> None:
5 if sys.platform == "win32":
6 assert separator() == "\\"
7 else:
8 assert separator() == "/"
tests/test_paths.py
1import sys
2
3import pytest
4
5
6@pytest.mark.parametrize(
7 ("platform", "expected"),
8 [
9 pytest.param("win32", "\\", id="windows"),
10 pytest.param("linux", "/", id="linux"),
11 ],
12)
13def test_separator(monkeypatch: pytest.MonkeyPatch, platform: str, expected: str) -> None:
14 monkeypatch.setattr(sys, "platform", platform)
15 assert separator() == expected
What it does¶
No fix is offered: splitting a test into cases needs knowledge of what each branch tests.
Active on¶
ifstatements,matchstatements and conditional expressions in a test function: a test function is a function or method whose name starts withtest, in a test module as defined in test-class. Each branch picks which check runs.Example: reported
tests/test_if.py1def test_separator() -> None: 2 if sys.platform == "win32": 3 assert separator() == "\\" 4 else: 5 assert separator() == "/"
tests/test_match.py1def test_kind(kind: str) -> None: 2 match kind: 3 case "a": 4 assert parse(kind) 5 case _: 6 assert not parse(kind)
tests/test_ternary.py1def test_length() -> None: 2 assert (1 if sys.platform == "win32" else 2) == separator_length()
Exceptions¶
Branches inside nested functions, classes and lambdas: they run only when that code is called, not as part of the test’s own path.
Example: not reported
tests/test_nested.py1def test_callbacks() -> None: 2 def check(value: int) -> None: 3 if value: 4 assert value 5 6 class Helper: 7 def pick(self, value: int) -> int: 8 return 1 if value else 2 9 10 assert (lambda value: 1 if value else 2)(0) == 2 11 check(1) 12 assert Helper().pick(0) == 2
ifclauses and conditional items in comprehensions: they shape the items a comprehension builds, rather than pick which check runs.Example: not reported
tests/test_comprehension.py1def test_filtered(items: list[int]) -> None: 2 assert [item for item in items if item] == [1, 2]
tests/test_comprehension_item.py1def test_labels(tags: list[str | None]) -> None: 2 assert [tag if tag is not None else "-" for tag in tags] == ["a", "-"]
Other tools¶
Sourcery flags conditionals in tests through no-conditionals-in-tests.