304 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
| import re
 | |
| import sys
 | |
| 
 | |
| import pytest
 | |
| from _pytest.outcomes import Failed
 | |
| from _pytest.pytester import Pytester
 | |
| 
 | |
| 
 | |
| class TestRaises:
 | |
|     def test_check_callable(self) -> None:
 | |
|         with pytest.raises(TypeError, match=r".* must be callable"):
 | |
|             pytest.raises(RuntimeError, "int('qwe')")  # type: ignore[call-overload]
 | |
| 
 | |
|     def test_raises(self):
 | |
|         excinfo = pytest.raises(ValueError, int, "qwe")
 | |
|         assert "invalid literal" in str(excinfo.value)
 | |
| 
 | |
|     def test_raises_function(self):
 | |
|         excinfo = pytest.raises(ValueError, int, "hello")
 | |
|         assert "invalid literal" in str(excinfo.value)
 | |
| 
 | |
|     def test_raises_does_not_allow_none(self):
 | |
|         with pytest.raises(ValueError, match="Expected an exception type or"):
 | |
|             # We're testing that this invalid usage gives a helpful error,
 | |
|             # so we can ignore Mypy telling us that None is invalid.
 | |
|             pytest.raises(expected_exception=None)  # type: ignore
 | |
| 
 | |
|     def test_raises_does_not_allow_empty_tuple(self):
 | |
|         with pytest.raises(ValueError, match="Expected an exception type or"):
 | |
|             pytest.raises(expected_exception=())
 | |
| 
 | |
|     def test_raises_callable_no_exception(self) -> None:
 | |
|         class A:
 | |
|             def __call__(self):
 | |
|                 pass
 | |
| 
 | |
|         try:
 | |
|             pytest.raises(ValueError, A())
 | |
|         except pytest.fail.Exception:
 | |
|             pass
 | |
| 
 | |
|     def test_raises_falsey_type_error(self) -> None:
 | |
|         with pytest.raises(TypeError):
 | |
|             with pytest.raises(AssertionError, match=0):  # type: ignore[call-overload]
 | |
|                 raise AssertionError("ohai")
 | |
| 
 | |
|     def test_raises_repr_inflight(self):
 | |
|         """Ensure repr() on an exception info inside a pytest.raises with block works (#4386)"""
 | |
| 
 | |
|         class E(Exception):
 | |
|             pass
 | |
| 
 | |
|         with pytest.raises(E) as excinfo:
 | |
|             # this test prints the inflight uninitialized object
 | |
|             # using repr and str as well as pprint to demonstrate
 | |
|             # it works
 | |
|             print(str(excinfo))
 | |
|             print(repr(excinfo))
 | |
|             import pprint
 | |
| 
 | |
|             pprint.pprint(excinfo)
 | |
|             raise E()
 | |
| 
 | |
|     def test_raises_as_contextmanager(self, pytester: Pytester) -> None:
 | |
|         pytester.makepyfile(
 | |
|             """
 | |
|             import pytest
 | |
|             import _pytest._code
 | |
| 
 | |
|             def test_simple():
 | |
|                 with pytest.raises(ZeroDivisionError) as excinfo:
 | |
|                     assert isinstance(excinfo, _pytest._code.ExceptionInfo)
 | |
|                     1/0
 | |
|                 print(excinfo)
 | |
|                 assert excinfo.type == ZeroDivisionError
 | |
|                 assert isinstance(excinfo.value, ZeroDivisionError)
 | |
| 
 | |
|             def test_noraise():
 | |
|                 with pytest.raises(pytest.raises.Exception):
 | |
|                     with pytest.raises(ValueError):
 | |
|                            int()
 | |
| 
 | |
|             def test_raise_wrong_exception_passes_by():
 | |
|                 with pytest.raises(ZeroDivisionError):
 | |
|                     with pytest.raises(ValueError):
 | |
|                            1/0
 | |
|         """
 | |
|         )
 | |
|         result = pytester.runpytest()
 | |
|         result.stdout.fnmatch_lines(["*3 passed*"])
 | |
| 
 | |
|     def test_does_not_raise(self, pytester: Pytester) -> None:
 | |
|         pytester.makepyfile(
 | |
|             """
 | |
|             from contextlib import nullcontext as does_not_raise
 | |
|             import pytest
 | |
| 
 | |
|             @pytest.mark.parametrize('example_input,expectation', [
 | |
|                 (3, does_not_raise()),
 | |
|                 (2, does_not_raise()),
 | |
|                 (1, 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 = pytester.runpytest()
 | |
|         result.stdout.fnmatch_lines(["*4 passed*"])
 | |
| 
 | |
|     def test_does_not_raise_does_raise(self, pytester: Pytester) -> None:
 | |
|         pytester.makepyfile(
 | |
|             """
 | |
|             from contextlib import nullcontext as does_not_raise
 | |
|             import pytest
 | |
| 
 | |
|             @pytest.mark.parametrize('example_input,expectation', [
 | |
|                 (0, 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 = pytester.runpytest()
 | |
|         result.stdout.fnmatch_lines(["*2 failed*"])
 | |
| 
 | |
|     def test_noclass(self) -> None:
 | |
|         with pytest.raises(TypeError):
 | |
|             pytest.raises("wrong", lambda: None)  # type: ignore[call-overload]
 | |
| 
 | |
|     def test_invalid_arguments_to_raises(self) -> None:
 | |
|         with pytest.raises(TypeError, match="unknown"):
 | |
|             with pytest.raises(TypeError, unknown="bogus"):  # type: ignore[call-overload]
 | |
|                 raise ValueError()
 | |
| 
 | |
|     def test_tuple(self):
 | |
|         with pytest.raises((KeyError, ValueError)):
 | |
|             raise KeyError("oops")
 | |
| 
 | |
|     def test_no_raise_message(self) -> None:
 | |
|         try:
 | |
|             pytest.raises(ValueError, int, "0")
 | |
|         except pytest.fail.Exception as e:
 | |
|             assert e.msg == f"DID NOT RAISE {repr(ValueError)}"
 | |
|         else:
 | |
|             assert False, "Expected pytest.raises.Exception"
 | |
| 
 | |
|         try:
 | |
|             with pytest.raises(ValueError):
 | |
|                 pass
 | |
|         except pytest.fail.Exception as e:
 | |
|             assert e.msg == f"DID NOT RAISE {repr(ValueError)}"
 | |
|         else:
 | |
|             assert False, "Expected pytest.raises.Exception"
 | |
| 
 | |
|     @pytest.mark.parametrize("method", ["function", "function_match", "with"])
 | |
|     def test_raises_cyclic_reference(self, method):
 | |
|         """Ensure pytest.raises does not leave a reference cycle (#1965)."""
 | |
|         import gc
 | |
| 
 | |
|         class T:
 | |
|             def __call__(self):
 | |
|                 raise ValueError
 | |
| 
 | |
|         t = T()
 | |
|         refcount = len(gc.get_referrers(t))
 | |
| 
 | |
|         if method == "function":
 | |
|             pytest.raises(ValueError, t)
 | |
|         elif method == "function_match":
 | |
|             pytest.raises(ValueError, t).match("^$")
 | |
|         else:
 | |
|             with pytest.raises(ValueError):
 | |
|                 t()
 | |
| 
 | |
|         # ensure both forms of pytest.raises don't leave exceptions in sys.exc_info()
 | |
|         assert sys.exc_info() == (None, None, None)
 | |
| 
 | |
|         assert refcount == len(gc.get_referrers(t))
 | |
| 
 | |
|     def test_raises_match(self) -> None:
 | |
|         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 = (
 | |
|             "Regex pattern did not match.\n"
 | |
|             f" Regex: {msg!r}\n"
 | |
|             " Input: \"invalid literal for int() with base 10: 'asdf'\""
 | |
|         )
 | |
|         with pytest.raises(AssertionError, match="(?m)" + re.escape(expr)):
 | |
|             with pytest.raises(ValueError, match=msg):
 | |
|                 int("asdf", base=10)
 | |
| 
 | |
|         # "match" without context manager.
 | |
|         pytest.raises(ValueError, int, "asdf").match("invalid literal")
 | |
|         with pytest.raises(AssertionError) as excinfo:
 | |
|             pytest.raises(ValueError, int, "asdf").match(msg)
 | |
|         assert str(excinfo.value) == expr
 | |
| 
 | |
|         pytest.raises(TypeError, int, match="invalid")
 | |
| 
 | |
|         def tfunc(match):
 | |
|             raise ValueError(f"match={match}")
 | |
| 
 | |
|         pytest.raises(ValueError, tfunc, match="asdf").match("match=asdf")
 | |
|         pytest.raises(ValueError, tfunc, match="").match("match=")
 | |
| 
 | |
|     def test_match_failure_string_quoting(self):
 | |
|         with pytest.raises(AssertionError) as excinfo:
 | |
|             with pytest.raises(AssertionError, match="'foo"):
 | |
|                 raise AssertionError("'bar")
 | |
|         (msg,) = excinfo.value.args
 | |
|         assert msg == '''Regex pattern did not match.\n Regex: "'foo"\n Input: "'bar"'''
 | |
| 
 | |
|     def test_match_failure_exact_string_message(self):
 | |
|         message = "Oh here is a message with (42) numbers in parameters"
 | |
|         with pytest.raises(AssertionError) as excinfo:
 | |
|             with pytest.raises(AssertionError, match=message):
 | |
|                 raise AssertionError(message)
 | |
|         (msg,) = excinfo.value.args
 | |
|         assert msg == (
 | |
|             "Regex pattern did not match.\n"
 | |
|             " Regex: 'Oh here is a message with (42) numbers in parameters'\n"
 | |
|             " Input: 'Oh here is a message with (42) numbers in parameters'\n"
 | |
|             " Did you mean to `re.escape()` the regex?"
 | |
|         )
 | |
| 
 | |
|     def test_raises_match_wrong_type(self):
 | |
|         """Raising an exception with the wrong type and match= given.
 | |
| 
 | |
|         pytest should throw the unexpected exception - the pattern match is not
 | |
|         really relevant if we got a different exception.
 | |
|         """
 | |
|         with pytest.raises(ValueError):
 | |
|             with pytest.raises(IndexError, match="nomatch"):
 | |
|                 int("asdf")
 | |
| 
 | |
|     def test_raises_exception_looks_iterable(self):
 | |
|         class Meta(type):
 | |
|             def __getitem__(self, item):
 | |
|                 return 1 / 0
 | |
| 
 | |
|             def __len__(self):
 | |
|                 return 1
 | |
| 
 | |
|         class ClassLooksIterableException(Exception, metaclass=Meta):
 | |
|             pass
 | |
| 
 | |
|         with pytest.raises(
 | |
|             Failed,
 | |
|             match=r"DID NOT RAISE <class 'raises(\..*)*ClassLooksIterableException'>",
 | |
|         ):
 | |
|             pytest.raises(ClassLooksIterableException, lambda: None)
 | |
| 
 | |
|     def test_raises_with_raising_dunder_class(self) -> None:
 | |
|         """Test current behavior with regard to exceptions via __class__ (#4284)."""
 | |
| 
 | |
|         class CrappyClass(Exception):
 | |
|             # Type ignored because it's bypassed intentionally.
 | |
|             @property  # type: ignore
 | |
|             def __class__(self):
 | |
|                 assert False, "via __class__"
 | |
| 
 | |
|         with pytest.raises(AssertionError) as excinfo:
 | |
|             with pytest.raises(CrappyClass()):  # type: ignore[call-overload]
 | |
|                 pass
 | |
|         assert "via __class__" in excinfo.value.args[0]
 | |
| 
 | |
|     def test_raises_context_manager_with_kwargs(self):
 | |
|         with pytest.raises(TypeError) as excinfo:
 | |
|             with pytest.raises(Exception, foo="bar"):  # type: ignore[call-overload]
 | |
|                 pass
 | |
|         assert "Unexpected keyword arguments" in str(excinfo.value)
 | |
| 
 | |
|     def test_expected_exception_is_not_a_baseexception(self) -> None:
 | |
|         with pytest.raises(TypeError) as excinfo:
 | |
|             with pytest.raises("hello"):  # type: ignore[call-overload]
 | |
|                 pass  # pragma: no cover
 | |
|         assert "must be a BaseException type, not str" in str(excinfo.value)
 | |
| 
 | |
|         class NotAnException:
 | |
|             pass
 | |
| 
 | |
|         with pytest.raises(TypeError) as excinfo:
 | |
|             with pytest.raises(NotAnException):  # type: ignore[type-var]
 | |
|                 pass  # pragma: no cover
 | |
|         assert "must be a BaseException type, not NotAnException" in str(excinfo.value)
 | |
| 
 | |
|         with pytest.raises(TypeError) as excinfo:
 | |
|             with pytest.raises(("hello", NotAnException)):  # type: ignore[arg-type]
 | |
|                 pass  # pragma: no cover
 | |
|         assert "must be a BaseException type, not str" in str(excinfo.value)
 |