Index: functional_tests/support/randomize/tests.py =================================================================== --- functional_tests/support/randomize/tests.py (revision 0) +++ functional_tests/support/randomize/tests.py (revision 0) @@ -0,0 +1,15 @@ +import unittest + +class Tests(unittest.TestCase): + + def test_A(self): + pass + + def test_B(self): + pass + + def test_C(self): + pass + + def test_D(self): + pass Index: functional_tests/test_randomize.py =================================================================== --- functional_tests/test_randomize.py (revision 0) +++ functional_tests/test_randomize.py (revision 0) @@ -0,0 +1,33 @@ +""" +Tests that plugins can override loadTestsFromTestCase +""" +import os +import unittest +from nose import loader +from nose.plugins import PluginTester +from nose.plugins.randomize import Randomize +from nose.plugins.base import Plugin + +support = os.path.join(os.path.dirname(__file__), 'support') + +class TestRandomizePlugin(PluginTester, unittest.TestCase): + + activate = '--randomize' + args = ['-v', '--seed=2530711073'] + plugins = [Randomize()] + suitepath = os.path.join(support, 'randomize') + + def runTest(self): + expect = [ + 'test_C (tests.Tests) ... ok', + 'test_B (tests.Tests) ... ok', + 'test_A (tests.Tests) ... ok', + 'test_D (tests.Tests) ... ok'] + print str(self.output) + for line in self.output: + if expect: + self.assertEqual(line.strip(), expect.pop(0)) + + +if __name__ == '__main__': + unittest.main() Index: nose/plugins/randomize.py =================================================================== --- nose/plugins/randomize.py (revision 0) +++ nose/plugins/randomize.py (revision 0) @@ -0,0 +1,100 @@ +""" +This plugin randomizes the order of tests within a unittest.TestCase class +""" +__test__ = False + +import logging +import os +from nose.plugins import Plugin +from nose import loader +from inspect import isfunction, ismethod +from nose.case import FunctionTestCase, MethodTestCase +from nose.failure import Failure +from nose.config import Config +from nose.importer import Importer, add_path, remove_path +from nose.selector import defaultSelector, TestAddress +from nose.util import cmp_lineno, getpackage, isclass, isgenerator, ispackage, \ + match_last, resolve_name, transplant_func, transplant_class +from nose.suite import ContextSuiteFactory, ContextList, LazySuite +import random +import unittest + +log = logging.getLogger(__name__) + +class Randomize(Plugin): + """ + Randomize the order of the tests within a unittest.TestCase class + """ + name = 'randomize' + # Generate a seed for deterministic behaviour + # Could use getstate and setstate, but that would involve + # pickling the state and storing it somewhere. too lazy. + seed = random.getrandbits(32) + + def options(self, parser, env): + """Register commandline options. + """ + Plugin.options(self, parser, env) + parser.add_option('--randomize', action='store_true', dest='randomize', + help="Randomize the order of the tests within a unittest.TestCase class") + parser.add_option('--seed', action='store', dest='seed', default=None, type = long, + help="Initialize the seed for deterministic behavior in reproducing failed tests") + + def configure(self, options, conf): + """ + Configure plugin. + """ + Plugin.configure(self, options, conf) + if options.randomize: + self.enabled = True + if options.seed is not None: + self.seed = options.seed + random.seed(self.seed) + print("Using %d as seed" % (self.seed,)) + + def makeTest(self, obj, parent=None): + + """Given a test object and its parent, return a test case + or test suite. + """ + ldr = loader.TestLoader() + if isinstance(obj, unittest.TestCase): + return obj + elif isclass(obj): + if parent and obj.__module__ != parent.__name__: + obj = transplant_class(obj, parent.__name__) + if issubclass(obj, unittest.TestCase): + # Randomize the order of the tests in the TestCase + return self.Randomized_loadTestsFromTestCase(obj) + else: + return ldr.loadTestsFromTestClass(obj) + elif ismethod(obj): + if parent is None: + parent = obj.__class__ + if issubclass(parent, unittest.TestCase): + return parent(obj.__name__) + else: + if isgenerator(obj): + return ldr.loadTestsFromGeneratorMethod(obj, parent) + else: + return MethodTestCase(obj) + elif isfunction(obj): + if parent and obj.__module__ != parent.__name__: + obj = transplant_func(obj, parent.__name__) + if isgenerator(obj): + return ldr.loadTestsFromGenerator(obj, parent) + else: + return FunctionTestCase(obj) + else: + return Failure(TypeError, + "Can't make a test from %s" % obj) + + def Randomized_loadTestsFromTestCase(self, testCaseClass): + l = loader.TestLoader() + tmp = l.loadTestsFromTestCase(testCaseClass) + randomized_tests = [] + for t in tmp._tests: + randomized_tests.append(t) + random.shuffle(randomized_tests) + tmp._tests = (t for t in randomized_tests) + return tmp Index: nose/plugins/setup_randomize.py =================================================================== --- nose/plugins/setup_randomize.py (revision 0) +++ nose/plugins/setup_randomize.py (revision 0) @@ -0,0 +1,27 @@ +""" +Randomize test order plugin +""" +try: + import ez_setup + ez_setup.use_setuptools() +except ImportError: + pass + +from setuptools import setup + +setup( + name='Randomize test order plugin', + version='0.1', + author='Charles McCreary', + author_email = 'charles.mccreary@tiawichiresearch.com', + description = 'Randomize the order of the tests within a unittest.TestCase class', + license = 'GNU LGPL', + py_modules = ['randomize'], + entry_points = { + 'nose.plugins.0.10': [ + 'randomize = randomize:Randomize' + ] + } + + ) +