From 06fae84b51f78000ada1489e55fa170fcfa1f0b6 Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Wed, 1 Feb 2017 13:37:13 +0100 Subject: [PATCH] add matching the error message to pytest.raises --- CHANGELOG.rst | 6 +++++- _pytest/python.py | 17 +++++++++++++++-- testing/python/raises.py | 13 +++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 006c0ed0f3d..a2299fea348 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,8 @@ 3.0.7 (unreleased) ======================= -* +* ``pytest.raises`` can now assert that the error message contains a certain text. + Thanks `@Kriechi`_ for the PR. * @@ -10,6 +11,9 @@ * +.. _@Kriechi: https://github.com/Kriechi + + 3.0.6 (2017-01-22) ================== diff --git a/_pytest/python.py b/_pytest/python.py index e46f2f1bcfb..082e4c56d4a 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1136,6 +1136,12 @@ def raises(expected_exception, *args, **kwargs): ... >>> assert str(exc_info.value) == "value must be <= 10" + Or you can use the keyword argument ``match_info`` to assert that the + exception contains a certain text:: + + >>> with raises(ValueError, match_info='must be 0'): + .... if value != 0: + .... raise ValueError("value must be 0 or None") Or you can specify a callable by passing a to-be-called lambda:: @@ -1191,11 +1197,15 @@ def raises(expected_exception, *args, **kwargs): raise TypeError(msg % type(expected_exception)) message = "DID NOT RAISE {0}".format(expected_exception) + match_info = None if not args: if "message" in kwargs: message = kwargs.pop("message") - return RaisesContext(expected_exception, message) + if "match_info" in kwargs: + match_info = kwargs.pop("match_info") + message += " with text '{0}'".format(match_info) + return RaisesContext(expected_exception, message, match_info) elif isinstance(args[0], str): code, = args assert isinstance(code, str) @@ -1219,9 +1229,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_info): self.expected_exception = expected_exception self.message = message + self.match_info = match_info self.excinfo = None def __enter__(self): @@ -1240,6 +1251,8 @@ def __exit__(self, *tp): exc_type, value, traceback = tp tp = exc_type, exc_type(value), traceback self.excinfo.__init__(tp) + if self.match_info and not self.match_info.lower() in str(self.excinfo).lower(): + pytest.fail(self.message) suppress_exception = issubclass(self.excinfo.type, self.expected_exception) if sys.version_info[0] == 2 and suppress_exception: sys.exc_clear() diff --git a/testing/python/raises.py b/testing/python/raises.py index 8f141cfa1a9..2d894ae5189 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -126,3 +126,16 @@ def __call__(self): for o in gc.get_objects(): assert type(o) is not T + + def test_raises_match_info(self): + msg = "with base 10" + with pytest.raises(ValueError, match_info=msg): + int('asdf') + + try: + with pytest.raises(ValueError, match_info=msg): + int('asdf', base=16) + except pytest.raises.Exception as e: + assert e.msg == "DID NOT RAISE {0} with text '{1}'".format(repr(ValueError), msg) + else: + assert False, "Expected pytest.raises.Exception"