When testing code that should emit a warning, having multiple method calls in the tested code makes it unclear which call is expected to emit the warning.
In Python with pytest, this refers to with pytest.warns(…): bodies that contain more than one statement, or a single statement with
multiple method invocations that could emit the expected warning.
When writing tests for warnings, clarity is essential. If you put several method calls inside a pytest.warns block, or chain multiple
invocations in the tested expression, it becomes ambiguous which specific call is expected to emit the warning.
For example, consider this code:
with pytest.warns(UserWarning):
get_item().process()
Here, both get_item() and process() could potentially emit a UserWarning. The test does not make it explicit
which method is expected to warn. The same ambiguity appears when setup and the code under test share the with block:
with pytest.warns(UserWarning):
setup()
do_work()
By isolating the method call you want to test, you make the test explicit and unambiguous. This improves readability and maintainability, and makes your test suite a better specification of expected behavior.
Writing tests with multiple method calls when testing for warnings reduces test quality:
When using pytest’s pytest.warns() context manager, extract intermediate method calls into separate variables before the assertion,
and move setup outside the with block. This ensures only the final method call that you want to test is inside the warning-testing
block.
import pytest
def test_process():
# Which method emits the warning: get_item() or process()?
with pytest.warns(UserWarning): # Noncompliant
get_item().process()
import pytest
def test_process():
item = get_item()
with pytest.warns(UserWarning):
item.process()
import warnings
import pytest
def setup():
pass
def test_multiple_statements():
with pytest.warns(UserWarning): # Noncompliant
setup()
warnings.warn("x", UserWarning)
import warnings
import pytest
def setup():
pass
def test_multiple_statements():
setup()
with pytest.warns(UserWarning):
warnings.warn("x", UserWarning)