Files
pytest2/testing/python/raises.py
Bruno Oliveira 1130b9f742 Fix the stubborn test about cyclic references left by pytest.raises
In Python 2, a context manager's __exit__() leaves sys.exc_info with the exception values even when it was supposed
to suppress the exception, so we explicitly call sys.exc_clear() which removes the traceback and allow the object
to be released.

Also updated the test to not depend on the immediate destruction of the object but instead to ensure it is not being
tracked as a cyclic reference.

Fix #1965
2016-11-08 22:20:27 -02:00

129 lines
3.9 KiB
Python

import pytest
import sys
class TestRaises:
def test_raises(self):
source = "int('qwe')"
excinfo = pytest.raises(ValueError, source)
code = excinfo.traceback[-1].frame.code
s = str(code.fullsource)
assert s == source
def test_raises_exec(self):
pytest.raises(ValueError, "a,x = []")
def test_raises_syntax_error(self):
pytest.raises(SyntaxError, "qwe qwe qwe")
def test_raises_function(self):
pytest.raises(ValueError, int, 'hello')
def test_raises_callable_no_exception(self):
class A:
def __call__(self):
pass
try:
pytest.raises(ValueError, A())
except pytest.raises.Exception:
pass
def test_raises_flip_builtin_AssertionError(self):
# we replace AssertionError on python level
# however c code might still raise the builtin one
from _pytest.assertion.util import BuiltinAssertionError # noqa
pytest.raises(AssertionError,"""
raise BuiltinAssertionError
""")
def test_raises_as_contextmanager(self, testdir):
testdir.makepyfile("""
from __future__ import with_statement
import py, 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 = testdir.runpytest()
result.stdout.fnmatch_lines([
'*3 passed*',
])
def test_noclass(self):
with pytest.raises(TypeError):
pytest.raises('wrong', lambda: None)
def test_tuple(self):
with pytest.raises((KeyError, ValueError)):
raise KeyError('oops')
def test_no_raise_message(self):
try:
pytest.raises(ValueError, int, '0')
except pytest.raises.Exception as e:
assert e.msg == "DID NOT RAISE {0}".format(repr(ValueError))
else:
assert False, "Expected pytest.raises.Exception"
try:
with pytest.raises(ValueError):
pass
except pytest.raises.Exception as e:
assert e.msg == "DID NOT RAISE {0}".format(repr(ValueError))
else:
assert False, "Expected pytest.raises.Exception"
def test_custom_raise_message(self):
message = "TEST_MESSAGE"
try:
with pytest.raises(ValueError, message=message):
pass
except pytest.raises.Exception as e:
assert e.msg == message
else:
assert False, "Expected pytest.raises.Exception"
@pytest.mark.parametrize('method', ['function', 'with'])
def test_raises_cyclic_reference(self, method):
"""
Ensure pytest.raises does not leave a reference cycle (#1965).
"""
import gc
class T(object):
def __call__(self):
raise ValueError
t = T()
if method == 'function':
pytest.raises(ValueError, t)
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)
del t
# ensure the t instance is not stuck in a cyclic reference
for o in gc.get_objects():
assert type(o) is not T