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:
- If there is a simpler way, perhaps subclassing
django.test.TestCase
but not editing settings.py or, if not; - What the lines above that say "Set up temporary django db" and "Tear down 开发者_StackOverflowtemporary django db" should be?
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()
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')
精彩评论