diff --git a/AUTHORS b/AUTHORS index 5c8cd9c9e..a4a41c942 100644 --- a/AUTHORS +++ b/AUTHORS @@ -27,6 +27,7 @@ Anthony Shaw Anthony Sottile Anton Lodder Antony Lee +Arel Cordero Armin Rigo Aron Coyle Aron Curzon diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 9b31d4e68..8babe13bf 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -4,6 +4,7 @@ import math import pprint import sys import warnings +from contextlib import contextmanager from decimal import Decimal from numbers import Number @@ -726,3 +727,32 @@ class RaisesContext(object): if self.match_expr is not None and suppress_exception: self.excinfo.match(self.match_expr) return suppress_exception + + +# builtin pytest.does_not_raise helper + + +@contextmanager +def does_not_raise(): + r""" + This context manager is a complement to ``pytest.raises()`` that does + *not* catch any exceptions raised by the code block. + + + This is essentially a no-op but is useful when + conditionally parameterizing tests that may or may not + raise an error. For example:: + + @pytest.mark.parametrize('example_input,expectation', [ + (3, does_not_raise()), + (2, does_not_raise()), + (1, does_not_raise()), + (0, raises(ZeroDivisionError)), + ]) + def test_division(example_input, expectation): + '''Test how much I know division.''' + with expectation: + assert (6 / example_input) is not None + """ + + yield diff --git a/src/pytest.py b/src/pytest.py index c0010f166..16ce2ad70 100644 --- a/src/pytest.py +++ b/src/pytest.py @@ -32,6 +32,7 @@ from _pytest.python import Instance from _pytest.python import Module from _pytest.python import Package from _pytest.python_api import approx +from _pytest.python_api import does_not_raise from _pytest.python_api import raises from _pytest.recwarn import deprecated_call from _pytest.recwarn import warns @@ -50,6 +51,7 @@ __all__ = [ "cmdline", "Collector", "deprecated_call", + "does_not_raise", "exit", "fail", "File", diff --git a/testing/python/raises.py b/testing/python/raises.py index 4ff0b51bc..4ba9c1ccb 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -94,6 +94,46 @@ class TestRaises(object): result = testdir.runpytest() result.stdout.fnmatch_lines(["*3 passed*"]) + def test_does_not_raise(self, testdir): + testdir.makepyfile( + """ + import pytest + import _pytest._code + + @pytest.mark.parametrize('example_input,expectation', [ + (3, pytest.does_not_raise()), + (2, pytest.does_not_raise()), + (1, pytest.does_not_raise()), + (0, pytest.raises(ZeroDivisionError)), + ]) + def test_division(example_input, expectation): + '''Test how much I know division.''' + with expectation: + assert (6 / example_input) is not None + """ + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines(["*4 passed*"]) + + def test_does_not_raise_does_raise(self, testdir): + testdir.makepyfile( + """ + import pytest + import _pytest._code + + @pytest.mark.parametrize('example_input,expectation', [ + (0, pytest.does_not_raise()), + (1, pytest.raises(ZeroDivisionError)), + ]) + def test_division(example_input, expectation): + '''Test how much I know division.''' + with expectation: + assert (6 / example_input) is not None + """ + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines(["*2 failed*"]) + def test_noclass(self): with pytest.raises(TypeError): pytest.raises("wrong", lambda: None)