Anonymous avatar Anonymous committed c539dbb Merge

Merged upstream

Comments (0)

Files changed (5)

     * growl notifications on test run start and stop
     * filter individual test methods using a regex
     * load test functions from modules as well as TestCases
-    * test generators
+    * test generators *and* parameterized tests
     * integration with the coverage module for coverage reporting
     * display the time of individual tests in verbose reports
     * display a progress indicator as tests are run ([39/430] format) in verbose reports
 useful in some situations. This can be fixed with the use of the optparse
 epilogue.
 
+`main` gains two new parameters: `configs` and `userConfig`. If configs is supplied it can be a string or sequence of strings. It will be used *instead* of loading a config file from the current project directory. If userConfig is False then the userConfig file won't be loaded. If userConfig is False and configs is not None (it can be an empty list) then the --no-plugins option is not available at the command line as the loading of plugins is being completely controlled from the `main(...)` call.
+
 The verbosity argument to the main function and the TextTestRunner can be passed in as strings ('quiet', 'normal' or 'verbose', which correspond to 0, 1, and 2) instead of just integers.
 
 unit2 (the default test runner) runs test discovery if invoked without any arguments.
 always-on = True
 enhanced = False
 
-[outcomes]
-treat-as-skip =
-    IOError
-    TodoError
-    TypeError
-treat-as-fail =
-    GlormpError
+[parameters]
+always-on = False

unittest2/config.py

         configs.append((userPlugins, userParser, userExcludedPlugins))
     
     
-    if not configLocations:
+    if configLocations is None:
         cfgPath = os.path.join(os.getcwd(), CFG_NAME)
         localParser, localPlugins, localExcludedPlugins = loadPluginsConfigFile(cfgPath)
         configs.append((localPlugins, localParser, localExcludedPlugins))

unittest2/main.py

     
     # defaults for testing
     failfast = catchbreak = buffer = progName = module = defaultTest = None
-    pluginsLoaded = verbosity = None
+    pluginsLoaded = verbosity = configs = None
+    userConfig = True
 
     def __init__(self, module='__main__', defaultTest=None,
                  argv=None, testRunner=None,
                  testLoader=loader.defaultTestLoader, exit=True,
-                 verbosity=None, failfast=None, catchbreak=None, buffer=None):
+                 verbosity=None, failfast=None, catchbreak=None, buffer=None,
+                 configs=None, userConfig=True):
         if isinstance(module, basestring):
             __import__(module)
             self.module = sys.modules[module]
         else:
             self.module = module
 
+        if isinstance(configs, basestring):
+            configs = (configs,)
+        self.configs = configs
+        self.userConfig = userConfig
 
         if isinstance(verbosity, basestring):
             # allow string verbosities not in the dictionary
         if isinstance(testLoader, type):
             testLoader = testLoader()
         self.testLoader = testLoader
-        self.progName = os.path.basename(argv[0])
+
+        # allow argv[0] to be None
+        self.progName = os.path.basename(argv[0] or '')
 
         self.parseArgs(argv)
         self.runTests()
         # an initial pass over command line options to load config files
         # and plugins
         parser = _ImperviousOptionParser()
-        parser.add_option('--config', dest='configLocations', action='append')
-        parser.add_option('--no-user-config', dest='noUserConfig', default=False,
-                          action='store_true')
-        parser.add_option('--no-plugins', dest='pluginsDisabled', default=False,
-                          action='store_true')
+        if self.configs is None:
+            parser.add_option('--config', dest='configLocations', action='append')
+        if self.userConfig:
+            parser.add_option('--no-user-config', dest='noUserConfig', default=False,
+                              action='store_true')
+        if self.configs is None or self.userConfig:
+            parser.add_option('--no-plugins', dest='pluginsDisabled', default=False,
+                              action='store_true')
 
         if TestProgram.pluginsLoaded:
             # only needed because we call several times during tests
 
         # we catch any optparse errors here as they will be
         # reraised on the second pass through
-        try:
-            options, _ = parser.parse_args(argv)
-        except optparse.OptionError:
-            pluginsDisabled = False
-            noUserConfig = False
-            configLocations = []
-        else:
-            pluginsDisabled = options.pluginsDisabled
-            noUserConfig = options.noUserConfig
-            configLocations = options.configLocations
+        noUserConfig = not self.userConfig
+        configLocations = self.configs
+        pluginsDisabled = False
+        if self.configs is None or self.userConfig:
+            try:
+                options, _ = parser.parse_args(argv)
+            except optparse.OptionError:
+                pluginsDisabled = False
+                configLocations = self.configs or []
+            else:
+                pluginsDisabled = options.pluginsDisabled
+                if not noUserConfig:
+                    noUserConfig = options.noUserConfig
+                if configLocations is None:
+                    configLocations = options.configLocations or None
 
         loadPlugins(pluginsDisabled, noUserConfig, configLocations)
         TestProgram.pluginsLoaded = True
         parser.add_option('-q', '--quiet', dest='quiet', default=False,
                           help='Quiet output', action='store_true')
         
-        parser.add_option('--config', dest='configLocations', action='append',
-                          help='Specify local config file location')
-        parser.add_option('--no-user-config', dest='noUserConfig', default=False,
-                          action='store_true',
-                          help="Don't use user config file")
-        parser.add_option('--no-plugins', dest='pluginsDisabled', default=False,
-                          action='store_true', help="Disable all plugins")
+        if self.configs is None:
+            parser.add_option('--config', dest='configLocations', action='append',
+                              help='Specify local config file location')
+        if self.userConfig:
+            parser.add_option('--no-user-config', dest='noUserConfig', default=False,
+                              action='store_true',
+                              help="Don't use user config file")
+        if self.configs is None or self.userConfig:
+            parser.add_option('--no-plugins', dest='pluginsDisabled', default=False,
+                              action='store_true', help="Disable all plugins")
         
         if self.failfast != False:
             parser.add_option('-f', '--failfast', dest='failfast', default=None,

unittest2/plugins/moduleloading.py

     func.testGenerator = True
     return func
 
+def params(*paramList):
+    def decorator(func):
+        func.paramList = paramList
+        return func
+    return decorator
+
+
 class Functions(Plugin):
     
     generatorsEnabled = False
+    parametersEnabled = False
     configSection = 'functions'
     commandLineSwitch = (None, 'functions', 'Load tests from functions')
 
                 if tearDown is not None:
                     args['tearDown'] = tearDown
                 
-                if (not self.generatorsEnabled or 
-                    getattr(obj, 'testGenerator', None) is None):
-                    case = FunctionTestCase(obj, **args)
-                    tests.append(case)
-                else:
+                paramList = getattr(obj, 'paramList', None)
+                isGenerator = getattr(obj, 'testGenerator', False)
+                if self.parametersEnabled and paramList is not None:
+                    for index, argSet in enumerate(paramList):
+                        def func(argSet=argSet, obj=obj):
+                            return obj(*argSet)
+                        name = '%s.%s' % (obj.__module__, obj.__name__)
+                        func_name = name_from_args(name, index, argSet)
+                        case = ParamsFunctionCase(func_name, func, **args)
+                        tests.append(case)
+                elif self.generatorsEnabled and isGenerator:
                     extras = list(obj())
                     name = '%s.%s' % (obj.__module__, obj.__name__)
                     def createTest(name):
                         return GeneratorFunctionCase(name, **args)
                     tests.extend(testsFromGenerator(name, extras, createTest))
+                else:
+                    case = FunctionTestCase(obj, **args)
+                    tests.append(case)
                 
         event.extraTests.extend(tests)
 
 
 class GeneratorFunctionCase(FunctionTestCase):
-
     def __init__(self, name, **args):
         self._name = name
         FunctionTestCase.__init__(self, None, **args)
 
     id = __str__ = __repr__
 
+class ParamsFunctionCase(FunctionTestCase):
+    def __init__(self, name, func, **args):
+        self._name = name
+        FunctionTestCase.__init__(self, func, **args)
+        
+    def __repr__(self):
+        return self._name
+
+    id = __str__ = __repr__
 
 class Generators(Plugin):
 
 
 def testsFromGenerator(name, tests, testCaseClass):
     for index, (func, args) in enumerate(tests):
-        summary = ', '.join(repr(arg) for arg in args)
-
-        method_name = '%s_%s\n%s' % (name, index + 1, summary[:79])
+        method_name = name_from_args(name, index, args)
         setattr(testCaseClass, method_name, None)
         instance = testCaseClass(method_name)
         delattr(testCaseClass, method_name)
             return func(*args)
         setattr(instance, method_name, method)
         yield instance
+
+def name_from_args(name, index, args):
+    summary = ', '.join(repr(arg) for arg in args)
+    return '%s_%s\n%s' % (name, index + 1, summary[:79])
+
+
+class Parameters(Plugin):
+    configSection = 'parameters'
+    commandLineSwitch = (None, 'params', 'Enable parameterised tests')
+
+    def pluginsLoaded(self, event):
+        Functions.parametersEnabled = True
+
+    def getTestCaseNames(self, event):
+        names = filter(event.isTestMethod, dir(event.testCase))
+        klass = event.testCase
+        for name in names:
+            method = getattr(klass, name)
+            paramList = getattr(method, 'paramList', None)
+            if paramList is None:
+                continue
+
+            event.excludedNames.append(name)
+            for index, args in enumerate(method.paramList):
+                def _method(self, method=method, args=args):
+                    return method(self, *args)
+                method_name = name_from_args(name, index, args)
+                setattr(klass, method_name, _method)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.