collection: Prevent non-function callables from being collected

Fixes issue 331
previously to this change the collection code would issue a warning for
when ever it encountered a variable that looked like a test but wasn't a
function saying that it wouldn't collect it because it wasn't a function.
This fixes the logic so that if that warning is issued it really isn't
collected.
However previously special cases existed to support tests that were
created using functools.wraps and functools.partial. So the condition for
issuing that warning has been updated to take that in to account

Also try the old way of detecting functions just for proper integration
with mock.path in python 2.7 the get_real_func returned the unbound method
This commit is contained in:
Michael Birtwell 2015-09-23 00:06:55 +01:00
parent 0c21533cc5
commit a3bda59a30
4 changed files with 33 additions and 2 deletions

View File

@ -51,6 +51,7 @@ Marc Schlaich
Mark Abramowitz
Markus Unterwaditzer
Martijn Faassen
Michael Birtwell
Michael Droettboom
Nicolas Delaby
Pieter Mulder

View File

@ -4,6 +4,9 @@
- fix #1035: collecting tests if test module level obj has __getattr__().
Thanks Suor for the report and Bruno Oliveira / Tom Viner for the PR.
- fix #331: don't collect tests if their failure cannot be reported correctly
e.g. they are a callable instance of a class.
2.8.2
-----

View File

@ -312,11 +312,14 @@ def pytest_pycollect_makeitem(collector, name, obj):
elif collector.istestfunction(obj, name):
# mock seems to store unbound methods (issue473), normalize it
obj = getattr(obj, "__func__", obj)
if not isfunction(obj):
# We need to try and unwrap the function if it's a functools.partial
# or a funtools.wrapped.
# We musn't if it's been wrapped with mock.patch (python 2 only)
if not (isfunction(obj) or isfunction(get_real_func(obj))):
collector.warn(code="C2", message=
"cannot collect %r because it is not a function."
% name, )
if getattr(obj, "__test__", True):
elif getattr(obj, "__test__", True):
if is_generator(obj):
res = Generator(name, parent=collector)
else:

View File

@ -1013,3 +1013,27 @@ def test_collect_functools_partial(testdir):
""")
result = testdir.inline_run()
result.assertoutcome(passed=6, failed=2)
def test_dont_collect_non_function_callable(testdir):
"""Test for issue https://github.com/pytest-dev/pytest/issues/331
In this case an INTERNALERROR occurred trying to report the failure of
a test like this one because py test failed to get the source lines.
"""
testdir.makepyfile("""
class Oh(object):
def __call__(self):
pass
test_a = Oh()
def test_real():
pass
""")
result = testdir.runpytest('-rw')
result.stdout.fnmatch_lines([
'*collected 1 item*',
'WC2 *',
'*1 passed, 1 pytest-warnings in *',
])