Ticket #4460: 4460.diff

File 4460.diff, 13.4 KB (added by Russell Keith-Magee, 17 years ago)

Code to allow invocation of individual test methods and suites

  • django/test/simple.py

     
    11import unittest
    22from django.conf import settings
     3from django.db.models import get_app, get_apps
    34from django.test import _doctest as doctest
    45from django.test.utils import setup_test_environment, teardown_test_environment
    56from django.test.utils import create_test_db, destroy_test_db
     
    1011   
    1112doctestOutputChecker = OutputChecker()
    1213
     14def get_tests(app_module):
     15    try:
     16        app_path = app_module.__name__.split('.')[:-1]
     17        test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
     18    except ImportError, e:
     19        # Couldn't import tests.py. Was it due to a missing file, or
     20        # due to an import error in a tests.py that actually exists?
     21        import os.path
     22        from imp import find_module
     23        try:
     24            mod = find_module(TEST_MODULE, [os.path.dirname(app_module.__file__)])
     25        except ImportError:
     26            # 'tests' module doesn't exist. Move on.
     27            test_module = None
     28        else:
     29            # The module exists, so there must be an import error in the
     30            # test module itself. We don't need the module; so if the
     31            # module was a single file module (i.e., tests.py), close the file
     32            # handle returned by find_module. Otherwise, the test module
     33            # is a directory, and there is nothing to close.
     34            if mod[0]:
     35                mod[0].close()
     36            raise
     37    return test_module
     38   
    1339def build_suite(app_module):
    1440    "Create a complete Django test suite for the provided application module"
    1541    suite = unittest.TestSuite()
     
    3056   
    3157    # Check to see if a separate 'tests' module exists parallel to the
    3258    # models module
    33     try:
    34         app_path = app_module.__name__.split('.')[:-1]
    35         test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
    36        
     59    test_module = get_tests(app_module)
     60    if test_module:
    3761        # Load unit and doctests in the tests.py module. If module has
    3862        # a suite() method, use it. Otherwise build the test suite ourselves.
    3963        if hasattr(test_module, 'suite'):
     
    4771            except ValueError:
    4872                # No doc tests in tests.py
    4973                pass
    50     except ImportError, e:
    51         # Couldn't import tests.py. Was it due to a missing file, or
    52         # due to an import error in a tests.py that actually exists?
    53         import os.path
    54         from imp import find_module
    55         try:
    56             mod = find_module(TEST_MODULE, [os.path.dirname(app_module.__file__)])
    57         except ImportError:
    58             # 'tests' module doesn't exist. Move on.
    59             pass
    60         else:
    61             # The module exists, so there must be an import error in the
    62             # test module itself. We don't need the module; so if the
    63             # module was a single file module (i.e., tests.py), close the file
    64             # handle returned by find_module. Otherwise, the test module
    65             # is a directory, and there is nothing to close.
    66             if mod[0]:
    67                 mod[0].close()
    68             raise
    69            
    7074    return suite
    7175
    72 def run_tests(module_list, verbosity=1, interactive=True, extra_tests=[]):
     76def build_test(label):
     77    """Construct a test case a test with the specified label. Label should
     78    be of the form model.TestClass or model.TestClass.test_method. Returns
     79    an instantiated test or test suite corresponding to the label provided.
     80       
    7381    """
    74     Run the unit tests for all the modules in the provided list.
    75     This testrunner will search each of the modules in the provided list,
    76     looking for doctests and unittests in models.py or tests.py within
    77     the module. A list of 'extra' tests may also be provided; these tests
     82    parts = label.split('.')
     83    if len(parts) < 2 or len(parts) > 3:
     84        raise ValueError("Test label '%s' should be of the form app.TestCase or app.TestCase.test_method" % label)
     85   
     86    app_module = get_app(parts[0])
     87    TestClass = getattr(app_module, parts[1], None)
     88
     89    # Couldn't find the test class in models.py; look in tests.py
     90    if TestClass is None:
     91        test_module = get_tests(app_module)
     92        if test_module:
     93            TestClass = getattr(test_module, parts[1], None)
     94
     95    if len(parts) == 2: # label is app.TestClass
     96        try:
     97            return unittest.TestLoader().loadTestsFromTestCase(TestClass)
     98        except TypeError:
     99            raise ValueError("Test label '%s' does not refer to a test class" % label)           
     100    else: # label is app.TestClass.test_method
     101        return TestClass(parts[2])
     102
     103def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]):
     104    """
     105    Run the unit tests for all the test labels in the provided list.
     106    Labels must be of the form:
     107     - app.TestClass.test_method
     108        Run a single specific test method
     109     - app.TestClass
     110        Run all the test methods in a given class
     111     - app
     112        Search for doctests and unittests in the named application.
     113
     114    When looking for tests, the test runner will look in the models and
     115    tests modules for the application.
     116   
     117    A list of 'extra' tests may also be provided; these tests
    78118    will be added to the test suite.
    79119   
    80120    Returns the number of tests that failed.
     
    83123   
    84124    settings.DEBUG = False   
    85125    suite = unittest.TestSuite()
    86      
    87     for module in module_list:
    88         suite.addTest(build_suite(module))
    89126   
     127    if test_labels:
     128        for label in test_labels:
     129            if '.' in label:
     130                suite.addTest(build_test(label))
     131            else:
     132                app = get_app(label)
     133                suite.addTest(build_suite(app))
     134    else:
     135        for app in get_apps():
     136            suite.addTest(build_suite(app))
     137   
    90138    for test in extra_tests:
    91139        suite.addTest(test)
    92140
  • django/core/management.py

     
    13311331    runfastcgi(args)
    13321332runfcgi.args = '[various KEY=val options, use `runfcgi help` for help]'
    13331333
    1334 def test(app_labels, verbosity=1, interactive=True):
     1334def test(test_labels, verbosity=1, interactive=True):
    13351335    "Runs the test suite for the specified applications"
    13361336    from django.conf import settings
    13371337    from django.db.models import get_app, get_apps
    1338 
    1339     if len(app_labels) == 0:
    1340         app_list = get_apps()
    1341     else:
    1342         app_list = [get_app(app_label) for app_label in app_labels]
    1343 
     1338   
    13441339    test_path = settings.TEST_RUNNER.split('.')
    13451340    # Allow for Python 2.5 relative paths
    13461341    if len(test_path) > 1:
     
    13501345    test_module = __import__(test_module_name, {}, {}, test_path[-1])
    13511346    test_runner = getattr(test_module, test_path[-1])
    13521347
    1353     failures = test_runner(app_list, verbosity=verbosity, interactive=interactive)
     1348    failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
    13541349    if failures:
    13551350        sys.exit(failures)
    13561351
  • tests/runtests.py

     
    7373        self.assert_(not unexpected, "Unexpected Errors: " + '\n'.join(unexpected))
    7474        self.assert_(not missing, "Missing Errors: " + '\n'.join(missing))
    7575
    76 def django_tests(verbosity, interactive, tests_to_run):
     76def django_tests(verbosity, interactive, test_labels):
    7777    from django.conf import settings
    7878
    7979    old_installed_apps = settings.INSTALLED_APPS
     
    109109            # if the model was named on the command line, or
    110110            # no models were named (i.e., run all), import
    111111            # this model and add it to the list to test.
    112             if not tests_to_run or model_name in tests_to_run:
     112            if not test_labels or model_name in set([label.split('.')[0] for label in test_labels]):
    113113                if verbosity >= 1:
    114114                    print "Importing model %s" % model_name
    115115                mod = load_app(model_label)
    116116                if mod:
    117117                    if model_label not in settings.INSTALLED_APPS:
    118118                        settings.INSTALLED_APPS.append(model_label)
    119                     test_models.append(mod)
    120119        except Exception, e:
    121120            sys.stderr.write("Error while importing %s:" % model_name + ''.join(traceback.format_exception(*sys.exc_info())[1:]))
    122121            continue
     
    125124    extra_tests = []
    126125    for model_dir, model_name in get_invalid_models():
    127126        model_label = '.'.join([model_dir, model_name])
    128         if not tests_to_run or model_name in tests_to_run:
     127        if not test_labels or model_name in test_labels:
    129128            extra_tests.append(InvalidModelTestCase(model_label))
    130129
    131130    # Run the test suite, including the extra validation tests.
    132131    from django.test.simple import run_tests
    133     failures = run_tests(test_models, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests)
     132    failures = run_tests(test_labels, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests)
    134133    if failures:
    135134        sys.exit(failures)
    136135
  • docs/testing.txt

     
    450450        def setUp(self):
    451451            # test definitions as before
    452452
     453        def testFluffyAnimals(self):
     454            # A test that uses the fixtures
     455
    453456At the start of each test case, before ``setUp()`` is run, Django will
    454457flush the database, returning the database the state it was in directly
    455458after ``syncdb`` was called. Then, all the named fixtures are installed.
     
    483486
    484487``assertContains(response, text, count=None, status_code=200)``
    485488    Assert that a response indicates that a page could be retrieved and
    486     produced the nominated status code, and that ``text`` in the content 
    487     of the response. If ``count`` is provided, ``text`` must occur exactly 
     489    produced the nominated status code, and that ``text`` in the content
     490    of the response. If ``count`` is provided, ``text`` must occur exactly
    488491    ``count`` times in the response.
    489492
    490493``assertFormError(response, form, field, errors)``
     
    571574
    572575    $ ./manage.py test animals
    573576
     577**New in Django development version:** If you use unit tests, you can be more
     578specific in the tests that are executed. To run a single test case in an
     579application (for example, the AnimalTestCase described previously), add the
     580name of the test case to the label on the command line::
     581
     582    $ ./manage.py test animals.AnimalTestCase
     583
     584**New in Django development version:**To run a single test method inside a
     585test case, add the name of the test method to the label::
     586
     587    $ ./manage.py test animals.AnimalTestCase.testFluffyAnimals
     588
    574589When you run your tests, you'll see a bunch of text flow by as the test
    575590database is created and models are initialized. This test database is
    576591created from scratch every time you run your tests.
     
    665680can call it anything you want. The only requirement is that it has the
    666681same arguments as the Django test runner:
    667682
    668 ``run_tests(module_list, verbosity=1, interactive=True, extra_tests=[])``
    669     The module list is the list of Python modules that contain the models to be
    670     tested. This is the same format returned by ``django.db.models.get_apps()``.
    671     The test runner should search these modules for tests to execute.
     683``run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[])``
     684    **New in Django development version:** ``test_labels`` is a list of
     685    strings describing the tests to be run. A test label can take one of
     686    three forms:
     687        * ``app.TestCase.test_method`` - Run a single test method in a test case
     688        * ``app.TestCase`` - Run all the test methods in a test case
     689        * ``app`` - Search for and run all tests in the named application.
     690    If ``test_labels`` has a value of ``None``, the test runner should run
     691    search for tests in all the applications in ``INSTALLED_APPS``.
    672692
    673693    Verbosity determines the amount of notification and debug information that
    674694    will be printed to the console; ``0`` is no output, ``1`` is normal output,
    675695    and ``2`` is verbose output.
    676696
    677     **New in Django development version** If ``interactive`` is ``True``, the
     697    **New in Django development version:** If ``interactive`` is ``True``, the
    678698    test suite may ask the user for instructions when the test suite is
    679699    executed. An example of this behavior would be asking for permission to
    680     delete an existing test database. If ``interactive`` is ``False, the 
     700    delete an existing test database. If ``interactive`` is ``False, the
    681701    test suite must be able to run without any manual intervention.
    682    
    683     ``extra_tests`` is a list of extra ``TestCase`` instances to add to the 
    684     suite that is executed by the test runner. These extra tests are run 
     702
     703    ``extra_tests`` is a list of extra ``TestCase`` instances to add to the
     704    suite that is executed by the test runner. These extra tests are run
    685705    in addition to those discovered in the modules listed in ``module_list``.
    686    
     706
    687707    This method should return the number of tests that failed.
    688708
    689709Testing utilities
Back to Top