This is an issue when test code verifies that a warning was emitted without specifying its type and without checking its message, or when it uses
the overly broad base Warning type without checking the message.
In Python with pytest, this refers to underspecified pytest.warns() usage, such as:
match=, for example pytest.warns()Warning without a match parameterMore specific warning types such as UserWarning or DeprecationWarning are not covered by this rule when used without
match=.
Omitting the warning type while providing an effective match= is not an issue, because pytest.warns defaults to
Warning and the message pattern narrows the expectation.
When testing that code emits a warning, it is not sufficient to verify only that some warning was raised. Tests should also validate the warning type or message to differentiate expected warnings from unexpected ones.
Consider a function that should emit a DeprecationWarning when given a legacy argument. If your test only checks that a warning
occurred, it cannot distinguish between:
DeprecationWarning indicating proper deprecation signalingUserWarning from a different code pathResourceWarning from a resource leakAll three scenarios would make the test pass, even though two represent unintended behavior.
In pytest, the following patterns lead to insufficiently specific warning testing:
pytest.warns() without an expected warning type and without match=pytest.warns(Warning) without match=The base Warning class is the root of the warning hierarchy and catches every warning subtype. More specific types such as
UserWarning or DeprecationWarning are accepted without match=.
Omitting the warning type while providing match= is accepted: pytest.warns defaults to Warning, and the
message pattern already narrows the expectation.
These patterns create fragile tests that may pass for the wrong reasons, allowing regressions to slip through undetected.
Tests that underspecify expected warnings can pass when an unrelated warning is emitted, leaving regressions undetected. Narrowing the expected type and message keeps warning tests as a reliable safety net.
Pass a specific warning type to pytest.warns. When using the base Warning type (explicitly or via the default), add
match= with a pattern for the message.
import warnings
import pytest
def test_warns_without_type():
with pytest.warns(): # Noncompliant: no expected warning type
warnings.warn("x", UserWarning)
def test_warns_broad_type():
with pytest.warns(Warning): # Noncompliant: base Warning without match=
warnings.warn("deprecated", DeprecationWarning)
import warnings
import pytest
def test_warns_without_type():
with pytest.warns(UserWarning):
warnings.warn("x", UserWarning)
def test_warns_broad_type():
# Base Warning is allowed when match= narrows the expected message
with pytest.warns(Warning, match="deprecated"):
warnings.warn("deprecated", DeprecationWarning)
def test_warns_match_only():
# Omitting the type is allowed when match= is provided
with pytest.warns(match="deprecated"):
warnings.warn("deprecated", DeprecationWarning)