Source code for nose2.plugins.loader.parameters

"""
Load tests from parameterized functions and methods.

This plugin implements :func:`getTestCaseNames`,
:func:`loadTestsFromModule`, and :func:`loadTestsFromName` to support
loading tests from parameterized test functions and methods.

To parameterize a function or test case method, use :func:`nose2.tools.params`.

To address a particular parameterized test via a command-line test name,
append a colon (':') followed by the index (*starting from 1*) of the
case you want to execute.

Such And The Parameters Plugin
------------------------------

The parameters plugin can work with the Such DSL, as long as the first argument
of the test function is the "case" argument, followed by the other parameters::

    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.assertTrue(isinstance(bar, int))

        @it.should('do bar and extra')
        @params((1, 2), (3, 4) ,(5, 6))
        def testExtraArg(case, bar, foo):
            case.assertTrue(isinstance(bar, int))
            case.assertTrue(isinstance(foo, int))

    it.createTests(globals())

"""

# This module contains some code copied from unittest2 and other code
# developed in reference to unittest2.
# unittest2 is Copyright (c) 2001-2010 Python Software Foundation; All
# Rights Reserved. See: http://docs.python.org/license.html

import functools
import logging
import sys
import types
import unittest

from nose2 import exceptions, util
from nose2.events import Plugin
from nose2.plugins.loader.testclasses import MethodTestCase

log = logging.getLogger(__name__)
__unittest = True


class ParamsFunctionCase(unittest.FunctionTestCase):
    def __init__(self, name, func, **args):
        self._funcName = name
        unittest.FunctionTestCase.__init__(self, func, **args)

    def __repr__(self):
        return self._funcName

    id = __str__ = __repr__


[docs] class Parameters(Plugin): """Loader plugin that loads parameterized tests""" alwaysOn = True configSection = "parameters" def registerInSubprocess(self, event): event.pluginClasses.append(self.__class__)
[docs] def getTestCaseNames(self, event): """Generate test case names for all parameterized methods""" log.debug("getTestCaseNames %s", event) for name, method in util.iter_attrs(event.testCase): if not event.isTestMethod(name): continue paramList = getattr(method, "paramList", None) if paramList is None: continue # exclude this method from normal collection event.excludedNames.append(name) # generate the methods to be loaded by the testcase loader self._generate(event, name, method, event.testCase)
def getTestMethodNames(self, event): return self.getTestCaseNames(event)
[docs] def loadTestsFromModule(self, event): """Load tests from parameterized test functions in the module""" module = event.module def is_test(obj): return obj.__name__.startswith(self.session.testMethodPrefix) and hasattr( obj, "paramList" ) tests = [] for _, obj in util.iter_attrs(module): if isinstance(obj, types.FunctionType) and is_test(obj): tests.extend(self._generateFuncTests(obj)) event.extraTests.extend(tests)
[docs] def loadTestsFromName(self, event): """Load parameterized test named on command line""" original_name = name = event.name module = event.module try: result = util.test_from_name(name, module) except (AttributeError, ImportError): event.handled = True return event.loader.failedLoadTests(name, sys.exc_info()) if result is None: # we can't find it - let the default case handle it return parent, obj, fqname, index = result if not hasattr(obj, "paramList"): return if ( index is None and not isinstance(parent, type) and not isinstance(obj, types.FunctionType) ): log.debug("Don't know how to load parameterized tests from %s", obj) return if ( parent and isinstance(parent, type) and issubclass(parent, unittest.TestCase) ): # parameterized method in test case names = self._generate(event, obj.__name__, obj, parent) tests = [parent(n) for n in names] elif parent and isinstance(parent, type): # parameterized method in test class names = self._generate(event, obj.__name__, obj, parent) tests = [MethodTestCase(parent)(name) for name in names] else: # parameterized func tests = list(self._generateFuncTests(obj)) if index is not None: try: tests = [tests[index - 1]] except IndexError: raise exceptions.TestNotFoundError(original_name) suite = event.loader.suiteClass() suite.addTests(tests) event.handled = True return suite
def _generate(self, event, name, method, testCaseClass): names = [] for index, argSet in enumerate_params(method.paramList): method_name = util.name_from_args(name, index, argSet) if not hasattr(testCaseClass, method_name): # not already generated def _method(self, method=method, argSet=argSet): return method(self, *argSet) _method = functools.update_wrapper(_method, method) delattr(_method, "paramList") setattr(testCaseClass, method_name, _method) names.append(method_name) return names def _generateFuncTests(self, obj): args = {} setUp = getattr(obj, "setUp", None) tearDown = getattr(obj, "tearDown", None) if setUp is not None: args["setUp"] = setUp if tearDown is not None: args["tearDown"] = tearDown for index, argSet in enumerate_params(obj.paramList): def func(argSet=argSet, obj=obj): return obj(*argSet) func = functools.update_wrapper(func, obj) delattr(func, "paramList") name = f"{obj.__module__}.{obj.__name__}" func_name = util.name_from_args(name, index, argSet) yield util.transplant_class(ParamsFunctionCase, obj.__module__)( func_name, func, **args )
def enumerate_params(paramList): for index, argSet in enumerate(paramList): if not isinstance(argSet, tuple): argSet = (argSet,) yield index, argSet