How to use pytest to assert NO Warning is raised

Viewed 6131

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. (dead link, the 3.2.* doc is not available anymore).

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')
3 Answers

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)

The documentation mentions two options: fixture recwarn or using context manager warnings.catch_warnings.

Use recwarn fixture:

import warnings

def test_no_warnings(recwarn):
    assert len(recwarn) == 0
    warnings.warn("Watch out!")
    assert len(recwarn) == 0 # Fails

Use warnings warnings.catch_warnings:

import warnings

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

        warnings.warn("Watch Out!") # Fails

Which to use?

The second approach (warnings.catch_warnings) has better error message while the first option (recwarn) can be more tailored as you can actually manipulate the warning message/category.

For example, a test could fail for specific warning classes:

import warnings

def test_no_future_warnings(recwarn):
    warnings.warn("Watch out!", UserWarning)
    warnings.warn("Watch out in the future!", FutureWarning)

    relevant_warnings = [wrn for wrn in recwarn if wrn.category in (FutureWarning,)]
    assert relevant_warnings == [] # Fails

Links to documentation

Related