2
Load tests from generators.
4
This plugin implements :func:`loadTestFromTestCase`,
5
:func:`loadTestsFromName` and :func:`loadTestFromModule` to enable
6
loading tests from generators.
8
Generators may be functions or methods in test cases. In either case,
9
they must yield a callable and arguments for that callable once for
10
each test they generate. The callable and arguments may all be in one
11
tuple, or the arguments may be grouped into a separate tuple::
17
To address a particular generated test via a command-line test name,
18
append a colon (':') followed by the index, *starting from 1*, of the
19
generated case you want to execute.
22
# This module contains some code copied from unittest2 and other code
23
# developed in reference to unittest2.
24
# unittest2 is Copyright (c) 2001-2010 Python Software Foundation; All
25
# Rights Reserved. See: http://docs.python.org/license.html
33
from nose2 import exceptions, util
34
from nose2.events import Plugin
35
from nose2.compat import unittest as ut2
38
log = logging.getLogger(__name__)
42
class Generators(Plugin):
44
"""Loader plugin that loads generator tests"""
46
configSection = 'generators'
48
def registerInSubprocess(self, event):
49
event.pluginClasses.append(self.__class__)
51
def unpack(self, generator):
52
for index, func_args in enumerate(generator):
54
func, args = func_args
55
if not isinstance(args, tuple):
57
yield index, (func, args)
59
func, args = func_args[0], func_args[1:]
60
yield index, (func, args)
62
def loadTestsFromTestCase(self, event):
63
"""Load generator tests from test case"""
64
log.debug('loadTestsFromTestCase %s', event.testCase)
65
testCaseClass = event.testCase
66
for name in dir(testCaseClass):
67
method = getattr(testCaseClass, name)
68
if (name.startswith(self.session.testMethodPrefix) and
69
hasattr(getattr(testCaseClass, name), '__call__') and
70
util.isgenerator(method)):
71
instance = testCaseClass(name)
72
event.extraTests.extend(
73
self._testsFromGenerator(
74
event, name, method(instance), testCaseClass)
77
def loadTestsFromTestClass(self, event):
78
testCaseClass = event.testCase
79
for name in dir(testCaseClass):
80
method = getattr(testCaseClass, name)
81
if (name.startswith(self.session.testMethodPrefix) and
82
hasattr(getattr(testCaseClass, name), '__call__') and
83
util.isgenerator(method)):
84
instance = testCaseClass()
85
event.extraTests.extend(
86
self._testsFromGeneratorMethod(
87
event, name, method, instance)
90
def getTestCaseNames(self, event):
91
"""Get generator test case names from test case class"""
92
log.debug('getTestCaseNames %s', event.testCase)
93
names = filter(event.isTestMethod, dir(event.testCase))
94
klass = event.testCase
96
method = getattr(klass, name)
97
if util.isgenerator(method):
98
event.excludedNames.append(name)
100
def getTestMethodNames(self, event):
101
return self.getTestCaseNames(event)
103
def loadTestsFromName(self, event):
104
"""Load tests from generator named on command line"""
105
original_name = name = event.name
106
module = event.module
108
result = util.test_from_name(name, module)
109
except (AttributeError, ImportError) as e:
111
return event.loader.failedLoadTests(name, e)
113
# we can't find it - let the default case handle it
116
parent, obj, name, index = result
117
if not util.isgenerator(obj):
120
if (index is None and not
121
isinstance(parent, type) and not
122
isinstance(obj, types.FunctionType)):
123
log.debug("Don't know how to load generator tests from %s", obj)
127
isinstance(parent, type) and
128
issubclass(parent, unittest.TestCase)):
129
# generator method in test case
130
instance = parent(obj.__name__)
132
self._testsFromGenerator(
133
event, obj.__name__, obj(instance), parent)
136
isinstance(parent, type)):
137
# generator method in test class
141
self._testsFromGeneratorMethod(event, name, method, instance)
145
tests = list(self._testsFromGeneratorFunc(event, obj))
147
if index is not None:
149
tests = [tests[index - 1]]
151
raise exceptions.TestNotFoundError(original_name)
153
suite = event.loader.suiteClass()
154
suite.addTests(tests)
158
def loadTestsFromModule(self, event):
159
"""Load tests from generator functions in a module"""
160
module = event.module
163
return (obj.__name__.startswith(self.session.testMethodPrefix)
164
and util.isgenerator(obj))
166
for name in dir(module):
167
obj = getattr(module, name)
168
if isinstance(obj, types.FunctionType) and is_test(obj):
169
tests.extend(self._testsFromGeneratorFunc(event, obj))
170
event.extraTests.extend(tests)
172
def _testsFromGenerator(self, event, name, generator, testCaseClass):
174
for index, (func, args) in self.unpack(generator):
175
method_name = util.name_from_args(name, index, args)
176
setattr(testCaseClass, method_name, None)
177
instance = testCaseClass(method_name)
178
delattr(testCaseClass, method_name)
180
def method(func=func, args=args):
182
method = functools.update_wrapper(method, func)
183
setattr(instance, method_name, method)
186
exc_info = sys.exc_info()
187
test_name = '%s.%s.%s' % (testCaseClass.__module__,
188
testCaseClass.__name__,
190
yield event.loader.failedLoadTests(test_name, exc_info)
192
def _testsFromGeneratorFunc(self, event, obj):
194
name = '%s.%s' % (obj.__module__, obj.__name__)
196
setUp = getattr(obj, 'setUp', None)
197
tearDown = getattr(obj, 'tearDown', None)
198
if setUp is not None:
199
args['setUp'] = setUp
200
if tearDown is not None:
201
args['tearDown'] = tearDown
203
def createTest(name):
204
return util.transplant_class(
205
GeneratorFunctionCase, obj.__module__)(name, **args)
206
for test in self._testsFromGenerator(event, name, extras, createTest):
209
def _testsFromGeneratorMethod(self, event, name, method, instance):
210
extras = list(method(instance))
211
name = "%s.%s.%s" % (instance.__class__.__module__,
212
instance.__class__.__name__,
215
setUp = getattr(instance, 'setUp', None)
216
tearDown = getattr(instance, 'tearDown', None)
217
if setUp is not None:
218
args['setUp'] = setUp
219
if tearDown is not None:
220
args['tearDown'] = tearDown
222
def createTest(name):
223
return util.transplant_class(
224
GeneratorMethodCase(instance.__class__),
225
instance.__class__.__module__)(name, **args)
226
for test in self._testsFromGenerator(event, name, extras, createTest):
230
class GeneratorFunctionCase(ut2.FunctionTestCase):
232
def __init__(self, name, **args):
233
self._funcName = name
234
ut2.FunctionTestCase.__init__(self, None, **args)
236
_testFunc = property(lambda self: getattr(self, self._funcName),
237
lambda self, func: None)
240
return self._funcName
242
id = __str__ = __repr__
245
def GeneratorMethodCase(cls):
246
class _GeneratorMethodCase(GeneratorFunctionCase):
249
def setUpClass(klass):
250
if hasattr(cls, 'setUpClass'):
254
def tearDownClass(klass):
255
if hasattr(cls, 'tearDownClass'):
257
return _GeneratorMethodCase