Login

UnitTesting without create/destroy database

Author:
crucialfelix
Posted:
February 11, 2009
Language:
Python
Version:
1.0
Tags:
unittest
Score:
-1 (after 1 ratings)

This test runner is invoked with its own command:

./manage.py quicktest {usual test args follow}

this creates a test database if it needs to and then DOES NOT delete it. subsequent uses of it start with the same database. this is for rapid test/development cycles usually running a single test. a single test can run in less than a second.

when you need to alter the schema simply use the normal ./manage.py test which will delete the old test database and create a fresh one.

it does not replace your normal test runner. it could probably be altered to even use your custom test runner.

there are improvements to be done, and it will be included with a small testy app soon.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# FIRST FILE
# management/commands/quicktest

from django.core.management.base import BaseCommand
from optparse import make_option
import sys

class Command(BaseCommand):
    option_list = BaseCommand.option_list + (
        make_option('--verbosity', action='store', dest='verbosity', default='1',
            type='choice', choices=['0', '1', '2'],
            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
        make_option('--noinput', action='store_false', dest='interactive', default=True,
            help='Tells Django to NOT prompt the user for input of any kind.'),
    )
    help = 'Runs the test suite, creating a test db IF NEEDED and NOT DESTROYING the test db afterwards.  Otherwise operates exactly as does test.'
    args = '[appname ...]'

    requires_model_validation = False

    def handle(self, *test_labels, **options):
        from django.conf import settings

        verbosity = int(options.get('verbosity', 1))
        interactive = options.get('interactive', True)

        # MODIFY THIS set to the module/file where you put the testyrunner
        test_path = 'testy.testyrunner.run_tests'.split('.')
        # Allow for Python 2.5 relative paths
        if len(test_path) > 1:
            test_module_name = '.'.join(test_path[:-1])
        else:
            test_module_name = '.'
        test_module = __import__(test_module_name, {}, {}, test_path[-1])
        test_runner = getattr(test_module, test_path[-1])

        failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
        if failures:
            sys.exit(failures)




# SECOND FILE
# testy/testyrunner.py  or wherever you want to put it

from django.test.simple import *

def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]):
    """
    worsk exactly as per normal test
    but only creates the test_db if it doesn't yet exist
    and does not destroy it when done
    tables are flushed and fixtures loaded between tests as per usual
    but if your schema has not changed then this saves significant amounts of time
    and speeds up the test cycle
    
    Run the unit tests for all the test labels in the provided list.
    Labels must be of the form:
     - app.TestClass.test_method
        Run a single specific test method
     - app.TestClass
        Run all the test methods in a given class
     - app
        Search for doctests and unittests in the named application.

    When looking for tests, the test runner will look in the models and
    tests modules for the application.
    
    A list of 'extra' tests may also be provided; these tests
    will be added to the test suite.
    
    Returns the number of tests that failed.
    """
    setup_test_environment()
    
    settings.DEBUG = False    
    suite = unittest.TestSuite()

    # http://code.djangoproject.com/attachment/ticket/5979/test_siteid_overwrite_r6720.patch
    settings.SITE_ID = 1

    if test_labels:
        for label in test_labels:
            if '.' in label:
                suite.addTest(build_test(label))
            else:
                app = get_app(label)
                suite.addTest(build_suite(app))
    else:
        for app in get_apps():
            suite.addTest(build_suite(app))
    
    for test in extra_tests:
        suite.addTest(test)

    from django.db.backends import creation
    old_name = settings.DATABASE_NAME
    if settings.TEST_DATABASE_NAME:
        test_database_name = settings.TEST_DATABASE_NAME
    else:
        test_database_name = creation.TEST_DATABASE_PREFIX + settings.DATABASE_NAME

    # does test db exist already ?
    from django.db import connection, DatabaseError
    settings.DATABASE_NAME = test_database_name
    try:
        cursor = connection.cursor()
    except DatabaseError, e:
        # db does not exist
        # juggling !  create_test_db switches the DATABASE_NAME to the TEST_DATABASE_NAME 
        settings.DATABASE_NAME = old_name
        connection.creation.create_test_db(verbosity, autoclobber=True)
    else:
        connection.close()

    tr = unittest.TextTestRunner(verbosity=verbosity)
    
    result = tr.run(suite)
    
    # but keep the test db
    settings.DATABASE_NAME = old_name

    teardown_test_environment()

    return len(result.failures) + len(result.errors)

More like this

  1. Fail Faster: unsafe_test Management Command by majgis 2 years, 7 months ago
  2. Unit Test Profiling by justquick 6 years, 1 month ago
  3. Forcing unit test runner to abort after failed test by simonbun 7 years, 10 months ago
  4. Unit Test Profiling for Django 1.3/1.4 by hoffmaje 2 years, 11 months ago
  5. Exclude Apps When Testing by cronosa 4 years, 6 months ago

Comments

Please login first before commenting.