Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to setup and teardown temporary django db for unit testing?

I would like to have a python module containing some unit tests that I can pass to hg bisect --command.

The unit tests are testing some functionality of a django app, but I don't think I can use hg bisect --command manage.py test mytestapp because mytestapp would have to be enabled in settings.py, and the edits to settings.py would be clobbered when hg bisect updates the working directory.

Therefore, I would like to know if something like the following is the best way to go:

import functools, os, sys, unittest

sys.path.append(path_to_myproject)
os.environ['DJANGO_SETTINGS_MODULE'] = 'myapp.settings'


def with_test_db(func):
    """Decorator to setup and teardown test db."""
    @functools.wraps
    def wrapper(*args, **kwargs):
        try:
            # Set up temporary django db
            func(*args, **kwargs)
        finally:
            # Tear down temporary django db


class TestCase(unittest.TestCase):

    @with_test_db
    def test(self):
        # Do some tests using the temporary django db
        self.fail('Mark this revision as bad.')


if '__main__' == __name__:
    unittest.main()

I should be most grateful if you could advise either:

  1. If there is a simpler way, perhaps subclassing django.test.TestCase but not editing settings.py or, if not;
  2. What the lines above that say "Set up temporary django db" and "Tear down temporary django db" should be?
like image 862
blokeley Avatar asked Mar 28 '10 15:03

blokeley


People also ask

What is setUp and tearDown in unit test?

When a setUp() method is defined, the test runner will run that method prior to each test. Likewise, if a tearDown() method is defined, the test runner will invoke that method after each test.

Where does Django store test database?

from the docs: When using SQLite, the tests will use an in-memory database by default (i.e., the database will be created in memory, bypassing the filesystem entirely!). The TEST dictionary in DATABASES offers a number of settings to configure your test database.

What is tearDown ()?

tearDown()Provides an opportunity to perform cleanup after each test method in a test case ends.


2 Answers

Cracked it. I now have one python file completely independent of any django app that can run unit tests with a test database:

#!/usr/bin/env python
"""Run a unit test and return result.

This can be used with `hg bisect`.
It is assumed that this file resides in the same dir as settings.py

"""

import os
from os.path import abspath, dirname
import sys
import unittest

# Set up django
project_dir = abspath(dirname(dirname(__file__)))
sys.path.insert(0, project_dir)
os.environ['DJANGO_SETTINGS_MODULE'] = 'myproject.settings'

from django.db import connection
from django.test import TestCase
from django.test.utils import setup_test_environment, teardown_test_environment

from myproject import settings
from myproject.myapp.models import MyModel


class MyTestCase(TestCase):

    def test_something(self):
        # A failed assertion will make unittest.main() return non-zero
        # which if used with `hg bisect` will mark the revision as bad
        self.assertEqual(0, len(MyModel.objects.all())) # and so on


if '__main__' == __name__:
    try:
        setup_test_environment()
        settings.DEBUG = False    
        verbosity = 0
        old_database_name = settings.DATABASE_NAME
        connection.creation.create_test_db(verbosity)
        unittest.main()
    finally:
        connection.creation.destroy_test_db(old_database_name, verbosity)
        teardown_test_environment()
like image 111
blokeley Avatar answered Oct 17 '22 01:10

blokeley


You must use the internal Django TestCase to do so.

from django.test import TestCase

class TestCase(TestCase):

    # before every call to setUp(), the db is automatically 
    # set back to the state is was after the first syncdb then
    # all these fixture files will be loaded in the db   
    fixtures = ['mammals.json', 'birds']

    # put whatever you want here, you don't need to call the
    # super()
    def setUp(self):
        # Test definitions as before.
        call_setup_methods()

    def test(self):
        # Do some tests using the temporary django db
        self.fail('Mark this revision as bad.')

It's fully compatible with unittest so your code don't need to change much.

You can learn more about django.test, fixtures, flush and loaddata commands.

If you do want to use a decorator to do the job, you can use the call_command to use in your python program any django command. e.g :

from django.core.management import call_command

call_command('flush', 'myapp')
call_command('loaddata', 'myapp')
like image 5
e-satis Avatar answered Oct 17 '22 02:10

e-satis