如何为单元测试设置和拆除临时Django数据库?

9 投票
2 回答
8131 浏览
提问于 2025-04-15 20:57

我想要一个包含一些单元测试的Python模块,这样我就可以把它传给 hg bisect --command 使用。

这些单元测试是用来测试一个Django应用的某些功能,但我觉得我不能用 hg bisect --command manage.py test mytestapp,因为 mytestapp 必须在settings.py中启用,而当 hg bisect 更新工作目录时,settings.py的修改会被覆盖。

所以,我想知道下面这样的做法是否是最好的选择:

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()

如果你能给我一些建议,我将非常感激:

  1. 是否有更简单的方法,比如说可以继承 django.test.TestCase 而不修改settings.py,或者如果没有的话;
  2. 上面提到的“设置临时Django数据库”和“拆除临时Django数据库”应该怎么写?

2 个回答

5

你必须使用Django内部的TestCase来完成这个任务。

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.')

它和unittest完全兼容,所以你的代码不需要做太多修改。

你可以了解更多关于 django.test、数据文件清空数据库加载数据 命令的信息。

如果你想用装饰器来完成这个工作,你可以使用 call_command 在你的Python程序中调用任何Django命令。例如:

from django.core.management import call_command

call_command('flush', 'myapp')
call_command('loaddata', 'myapp')
10

我搞定了!现在我有一个完全独立于任何Django应用的Python文件,可以用测试数据库运行单元测试:

#!/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()

撰写回答