allow error message matching in pytest.raises
This commit is contained in:
parent
3b47cb45e6
commit
43662ce789
|
@ -13,6 +13,9 @@ New Features
|
|||
* ``pytest.warns`` now checks for subclass relationship rather than
|
||||
class equality. Thanks `@lesteve`_ for the PR (`#2166`_)
|
||||
|
||||
* ``pytest.raises`` now asserts that the error message matches a text or regex
|
||||
with the ``match`` keyword argument. Thanks `@Kriechi`_ for the PR.
|
||||
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
@ -56,6 +59,7 @@ Changes
|
|||
.. _@fogo: https://github.com/fogo
|
||||
.. _@mandeep: https://github.com/mandeep
|
||||
.. _@unsignedint: https://github.com/unsignedint
|
||||
.. _@Kriechi: https://github.com/Kriechi
|
||||
|
||||
.. _#1512: https://github.com/pytest-dev/pytest/issues/1512
|
||||
.. _#1874: https://github.com/pytest-dev/pytest/pull/1874
|
||||
|
|
|
@ -1134,7 +1134,7 @@ def raises(expected_exception, *args, **kwargs):
|
|||
>>> with raises(ValueError) as exc_info:
|
||||
... if value > 10:
|
||||
... raise ValueError("value must be <= 10")
|
||||
... assert str(exc_info.value) == "value must be <= 10" # this will not execute
|
||||
... assert exc_info.type == ValueError # this will not execute
|
||||
|
||||
Instead, the following approach must be taken (note the difference in
|
||||
scope)::
|
||||
|
@ -1143,7 +1143,16 @@ def raises(expected_exception, *args, **kwargs):
|
|||
... if value > 10:
|
||||
... raise ValueError("value must be <= 10")
|
||||
...
|
||||
>>> assert str(exc_info.value) == "value must be <= 10"
|
||||
>>> assert exc_info.type == ValueError
|
||||
|
||||
Or you can use the keyword argument ``match`` to assert that the
|
||||
exception matches a text or regex::
|
||||
|
||||
>>> with raises(ValueError, match='must be 0 or None'):
|
||||
... raise ValueError("value must be 0 or None")
|
||||
|
||||
>>> with raises(ValueError, match=r'must be \d+$'):
|
||||
... raise ValueError("value must be 42")
|
||||
|
||||
|
||||
Or you can specify a callable by passing a to-be-called lambda::
|
||||
|
@ -1194,11 +1203,15 @@ def raises(expected_exception, *args, **kwargs):
|
|||
raise TypeError(msg % type(expected_exception))
|
||||
|
||||
message = "DID NOT RAISE {0}".format(expected_exception)
|
||||
match_expr = None
|
||||
|
||||
if not args:
|
||||
if "message" in kwargs:
|
||||
message = kwargs.pop("message")
|
||||
return RaisesContext(expected_exception, message)
|
||||
if "match" in kwargs:
|
||||
match_expr = kwargs.pop("match")
|
||||
message += " matching '{0}'".format(match_expr)
|
||||
return RaisesContext(expected_exception, message, match_expr)
|
||||
elif isinstance(args[0], str):
|
||||
code, = args
|
||||
assert isinstance(code, str)
|
||||
|
@ -1222,9 +1235,10 @@ def raises(expected_exception, *args, **kwargs):
|
|||
pytest.fail(message)
|
||||
|
||||
class RaisesContext(object):
|
||||
def __init__(self, expected_exception, message):
|
||||
def __init__(self, expected_exception, message, match_expr):
|
||||
self.expected_exception = expected_exception
|
||||
self.message = message
|
||||
self.match_expr = match_expr
|
||||
self.excinfo = None
|
||||
|
||||
def __enter__(self):
|
||||
|
@ -1246,6 +1260,8 @@ class RaisesContext(object):
|
|||
suppress_exception = issubclass(self.excinfo.type, self.expected_exception)
|
||||
if sys.version_info[0] == 2 and suppress_exception:
|
||||
sys.exc_clear()
|
||||
if self.match_expr:
|
||||
self.excinfo.match(self.match_expr)
|
||||
return suppress_exception
|
||||
|
||||
|
||||
|
|
|
@ -118,3 +118,18 @@ class TestRaises:
|
|||
for o in gc.get_objects():
|
||||
assert type(o) is not T
|
||||
|
||||
|
||||
def test_raises_match(self):
|
||||
msg = r"with base \d+"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
int('asdf')
|
||||
|
||||
msg = "with base 10"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
int('asdf')
|
||||
|
||||
msg = "with base 16"
|
||||
expr = r"Pattern '{}' not found in 'invalid literal for int\(\) with base 10: 'asdf''".format(msg)
|
||||
with pytest.raises(AssertionError, match=expr):
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
int('asdf', base=10)
|
||||
|
|
|
@ -112,10 +112,9 @@ class TestDeprecatedCall(object):
|
|||
pytest.deprecated_call(self.dep_explicit, 0)
|
||||
|
||||
def test_deprecated_call_as_context_manager_no_warning(self):
|
||||
with pytest.raises(pytest.fail.Exception) as ex:
|
||||
with pytest.raises(pytest.fail.Exception, matches='^DID NOT WARN'):
|
||||
with pytest.deprecated_call():
|
||||
self.dep(1)
|
||||
assert str(ex.value).startswith("DID NOT WARN")
|
||||
|
||||
def test_deprecated_call_as_context_manager(self):
|
||||
with pytest.deprecated_call():
|
||||
|
|
Loading…
Reference in New Issue