From a3bda59a30afaf1f1b77ccdacf08ef8144129465 Mon Sep 17 00:00:00 2001 From: Michael Birtwell Date: Wed, 23 Sep 2015 00:06:55 +0100 Subject: [PATCH] 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 --- AUTHORS | 1 + CHANGELOG | 3 +++ _pytest/python.py | 7 +++++-- testing/python/collect.py | 24 ++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 83ca41159..141a7423c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -51,6 +51,7 @@ Marc Schlaich Mark Abramowitz Markus Unterwaditzer Martijn Faassen +Michael Birtwell Michael Droettboom Nicolas Delaby Pieter Mulder diff --git a/CHANGELOG b/CHANGELOG index 49af784b0..d699bdd84 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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 ----- diff --git a/_pytest/python.py b/_pytest/python.py index b194031c7..b7a737a94 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -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: diff --git a/testing/python/collect.py b/testing/python/collect.py index 228d58364..b78374fd5 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -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 *', + ])