How to use pytest to assert NO Warning is raised

Question:

I want to ensure that no warning at all is raised in one assertion.

Could not find any explicit answer in pytest documentation about warnings.

I’ve tried this, thinking maybe None would mean “nothing”:

def test_AttrStr_parse_warnings():
    """Check _AttrStr.parse() raises proper warnings in proper cases."""
    with pytest.warns(None):
        _AttrStr('').parse()

but this assertion is also always correct, for instance, the test does not fail, even if a warning is actually raised:

def test_AttrStr_parse_warnings():
    """Check _AttrStr.parse() raises proper warnings in proper cases."""
    with pytest.warns(None):
        _AttrStr('').parse()
        warnings.warn('any message')
Asked By: zezollo

||

Answers:

For pytest >= 7.0

The doc now explicitely mentions this case should be solved this way (without pytest):

with warnings.catch_warnings():
    warnings.simplefilter("error")
    ...

though this may not completely solve some cases (dynamic checks: see this post).

The solution suggested for pytest < 7.0, below, now raises a DeprecationWarning. Thanks to @Warren-Weckesser for signaling this in comment!

Possible solution for pytest < 7.0

Yet it was not planned to be used like this, it’s possible to "record" any possible warning raised, and use this to add another assertion to ensure the number of raised warnings is 0:

def test_AttrStr_parse_warnings():
    """Check parse() raises proper warnings in proper cases."""
    with pytest.warns(None) as record:
        _AttrStr('').parse()
    assert len(record) == 0

To ensure it works: adding warnings.warn('any message') in the second assertion let the test fail.

Answered By: zezollo

If you have tests that are testing other functionality, but you also want to assert that no warnings were raised you could use a decorator. Here’s one that I wrote based on the previous accepted answer from zezollo

def no_warnings(func):

    def wrapper_no_warnings(*args, **kwargs):

        with pytest.warns(None) as warnings:
            func(*args, **kwargs)

        if len(warnings) > 0:
            raise AssertionError(
                "Warnings were raised: " + ", ".join([str(w) for w in warnings])
            )

    return wrapper_no_warnings

You can then decorate test class functions to add this assertion.

class MyTestClass(TestCase)

  @no_warnings
  def test_something(self):

      # My important test
      self.assertTrue(True)
Answered By: JGC