Skip to content

Commit

Permalink
Merge pull request nose-devs#279 from little-dude/loading
Browse files Browse the repository at this point in the history
better errors when tests fail to load
  • Loading branch information
little-dude committed Feb 29, 2016
2 parents d62a635 + 1d7b4b4 commit b2da9b5
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 51 deletions.
13 changes: 7 additions & 6 deletions nose2/plugins/loader/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ def handle_dir(self, event, full_path, top_level):
yield result
self.event_handled = True
return

evt = events.MatchPathEvent(dirname, full_path, pattern)
result = self.session.hooks.matchDirPath(evt)
if evt.handled and not result:
self.event_handled = True


class Discoverer(object):

def loadTestsFromName(self, event):
"""Load tests from module named by event.name"""
# turn name into path or module name
Expand All @@ -73,7 +73,9 @@ def loadTestsFromName(self, event):
# try name as a dotted module name first
__import__(name)
module = sys.modules[name]
except ImportError:
except (KeyboardInterrupt, SystemExit):
raise
except:
# if that fails, try it as a file or directory
event.extraTests.extend(
self._find_tests(event, name, top_level_dir))
Expand Down Expand Up @@ -121,9 +123,8 @@ def _discover(self, event):
try:
start_dir, top_level_dir = self._getStartDirs()
except (OSError, ImportError):
_, ev, _ = sys.exc_info()
return loader.suiteClass(
loader.failedLoadTests(self.session.startDir, ev))
loader.failedLoadTests(self.session.startDir, sys.exc_info()))
log.debug("_discover in %s (%s)", start_dir, top_level_dir)
tests = list(self._find_tests(event, start_dir, top_level_dir))
return loader.suiteClass(tests)
Expand All @@ -143,7 +144,7 @@ def _find_tests(self, event, start, top_level):
for test in self._find_tests_in_file(
event, start, full_path, top_level):
yield test

def _find_tests_in_dir(self, event, full_path, top_level):
if not os.path.isdir(full_path):
return
Expand Down
5 changes: 3 additions & 2 deletions nose2/plugins/loader/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def teardown():
# Rights Reserved. See: http://docs.python.org/license.html


import sys
import inspect
import types

Expand All @@ -79,9 +80,9 @@ def loadTestsFromName(self, event):
module = event.module
try:
result = util.test_from_name(name, module)
except (AttributeError, ImportError) as e:
except (AttributeError, ImportError):
event.handled = True
return event.loader.failedLoadTests(name, e)
return event.loader.failedLoadTests(name, sys.exc_info())
if result is None:
return

Expand Down
4 changes: 2 additions & 2 deletions nose2/plugins/loader/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ def loadTestsFromName(self, event):
module = event.module
try:
result = util.test_from_name(name, module)
except (AttributeError, ImportError) as e:
except (AttributeError, ImportError):
event.handled = True
return event.loader.failedLoadTests(name, e)
return event.loader.failedLoadTests(name, sys.exc_info())
if result is None:
# we can't find it - let the default case handle it
return
Expand Down
11 changes: 6 additions & 5 deletions nose2/plugins/loader/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@
from nose2.tools import such
from nose2.tools.params import params
with such.A('foo') as it:
@it.should('do bar')
@params(1,2,3)
def test(case, bar):
case.assert_(isinstance(bar, int))
@it.should('do bar and extra')
@params((1, 2), (3, 4) ,(5, 6))
def testExtraArg(case, bar, foo):
case.assert_(isinstance(bar, int))
case.assert_(isinstance(foo, int))
it.createTests(globals())
"""
Expand All @@ -40,6 +40,7 @@ def testExtraArg(case, bar, foo):
# unittest2 is Copyright (c) 2001-2010 Python Software Foundation; All
# Rights Reserved. See: http://docs.python.org/license.html

import sys
import functools
import logging
import types
Expand Down Expand Up @@ -116,9 +117,9 @@ def loadTestsFromName(self, event):
module = event.module
try:
result = util.test_from_name(name, module)
except (AttributeError, ImportError) as e:
except (AttributeError, ImportError):
event.handled = True
return event.loader.failedLoadTests(name, e)
return event.loader.failedLoadTests(name, sys.exc_info())
if result is None:
# we can't find it - let the default case handle it
return
Expand Down
5 changes: 3 additions & 2 deletions nose2/plugins/loader/testcases.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# code developed in reference to that module and others within unittest2.
# unittest2 is Copyright (c) 2001-2010 Python Software Foundation; All
# Rights Reserved. See: http://docs.python.org/license.html
import sys
import logging
import unittest

Expand Down Expand Up @@ -54,9 +55,9 @@ def loadTestsFromName(self, event):
log.debug("load %s from %s", name, module)
try:
result = util.test_from_name(name, module)
except (AttributeError, ImportError) as e:
except (AttributeError, ImportError):
event.handled = True
return event.loader.failedLoadTests(name, e)
return event.loader.failedLoadTests(name, sys.exc_info())
if result is None:
return
parent, obj, name, index = result
Expand Down
7 changes: 3 additions & 4 deletions nose2/plugins/loader/testclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ def loadTestsFromName(self, event):
module = event.module
try:
result = util.test_from_name(name, module)
except (AttributeError, ImportError) as e:
except (AttributeError, ImportError):
event.handled = True
return event.loader.failedLoadTests(name, e)
return event.loader.failedLoadTests(name, sys.exc_info())
if result is None:
return
parent, obj, name, index = result
Expand Down Expand Up @@ -149,9 +149,8 @@ def _loadTestsFromTestClass(self, event, cls):
MethodTestCase(cls), cls.__module__)(name)
for name in names])
except:
_, ev, _ = sys.exc_info()
return event.loader.suiteClass(
event.loader.failedLoadTests(cls.__name__, ev))
event.loader.failedLoadTests(cls.__name__, sys.exc_info()))
if evt.extraTests:
loaded_suite.addTests(evt.extraTests)
# ... add extra tests
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from nose2.compat import unittest


def test_foo():
pass


class TestFoo(unittest.TestCase):

def test_foo(self):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
raise ValueError('booms')

import unittest


def test():
pass


class Test(unittest.TestCase):

def test(self):
pass
55 changes: 53 additions & 2 deletions nose2/tests/functional/test_loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ def test_import_error_module(self):
'scenario/module_import_err',
'-v',
'test_import_err')
self.assertTestRunOutputMatches(proc, stderr='ImportError: booms')
self.assertTestRunOutputMatches(proc, stderr='Traceback \(most recent call last\):')
self.assertTestRunOutputMatches(proc, stderr='Ran 1 test')
self.assertEqual(proc.poll(), 1)

Expand All @@ -229,6 +231,8 @@ def test_import_error_func(self):
'scenario/module_import_err',
'-v',
'test_import_err.test')
self.assertTestRunOutputMatches(proc, stderr='ImportError: booms')
self.assertTestRunOutputMatches(proc, stderr='Traceback \(most recent call last\):')
self.assertTestRunOutputMatches(proc, stderr='Ran 1 test')
self.assertEqual(proc.poll(), 1)

Expand All @@ -237,6 +241,8 @@ def test_import_error_testcase(self):
'scenario/module_import_err',
'-v',
'test_import_err.Test')
self.assertTestRunOutputMatches(proc, stderr='ImportError: booms')
self.assertTestRunOutputMatches(proc, stderr='Traceback \(most recent call last\):')
self.assertTestRunOutputMatches(proc, stderr='Ran 1 test')
self.assertEqual(proc.poll(), 1)

Expand All @@ -245,9 +251,54 @@ def test_import_error_testcase_method(self):
'scenario/module_import_err',
'-v',
'test_import_err.Test.test')
self.assertTestRunOutputMatches(proc, stderr='ImportError: booms')
self.assertTestRunOutputMatches(proc, stderr='Traceback \(most recent call last\):')
self.assertTestRunOutputMatches(proc, stderr='Ran 1 test')
self.assertEqual(proc.poll(), 1)

def test_import_error_package(self):
proc = self.runIn(
'scenario/module_import_err',
'-v',
'pkg')
# In this case, there should not be an AttributeError since we only
# import pkg, which should work.
self.assertTestRunOutputMatches(proc, stderr='ImportError: Failed to import test module: pkg.test_import_err')
self.assertTestRunOutputMatches(proc, stderr='Traceback \(most recent call last\):')
self.assertTestRunOutputMatches(proc, stderr='ValueError: booms')
# 3 tests should run, and only one should fail. We're testing that a
# loading error does not prevent from running other tests.
self.assertTestRunOutputMatches(proc, stderr='Ran 3 tests')
self.assertTestRunOutputMatches(proc, stderr='FAILED \(errors=1\)')
self.assertEqual(proc.poll(), 1)

def test_import_error_module_in_package(self):
proc = self.runIn(
'scenario/module_import_err',
'-v',
'pkg.test_import_err')
# In this case, there should not be an ImportError. The import of
# pkg.test_import_err fails due with a `ValueError`, and this is the
# one we are expecting.
self.assertTestRunOutputMatches(proc, stderr='AttributeError: ')
self.assertTestRunOutputMatches(proc, stderr='ValueError: booms')
self.assertTestRunOutputMatches(proc, stderr='Ran 1 test')
self.assertEqual(proc.poll(), 1)

def test_import_error_unknown_module_in_package(self):
proc = self.runIn(
'scenario/module_import_err',
'-v',
'pkg.test_does_not_exit')
# In this case, there should not be an ImportError. The import of
# pkg.test_import_err fails due with a `ValueError`, and this is the
# one we are expecting.
self.assertTestRunOutputMatches(proc, stderr='AttributeError: ')
self.assertTestRunOutputMatches(proc, stderr='ImportError: No module named')
self.assertTestRunOutputMatches(proc, stderr='Ran 1 test')
self.assertEqual(proc.poll(), 1)



class TestTestClassLoading(FunctionalTestCase):

Expand Down Expand Up @@ -300,11 +351,11 @@ def test_error_in_test_class(self):
self.assertTestRunOutputMatches(proc, stderr='Ran 1 test')
self.assertTestRunOutputMatches(proc, stderr='FAILED')
self.assertEqual(proc.poll(), 1)

def test_expected_failures(self):
proc = self.runIn(
'scenario/expected_failures',
'-v',
'expected_failures')
self.assertTestRunOutputMatches(proc, stderr=r'FAILED \(failures=1, expected failures=1, unexpected successes=1\)')

Loading

0 comments on commit b2da9b5

Please sign in to comment.