加快Django测试速度

20 投票
8 回答
6842 浏览
提问于 2025-04-16 04:37

我想了解一下你们在Django中进行测试的流程。

背景信息:http://docs.djangoproject.com/en/dev/topics/testing/

我在使用测试驱动开发时遇到了一些困难。Django的测试运行器每次启动时都会在一个测试数据库中创建所有的数据库模型。对于我们当前的项目(模型数量在40到240之间),这意味着测试启动时通常需要20秒。

这让我们很难频繁测试新功能。我的问题是,你们是怎么解决这个问题的呢?

我过去尝试过几种方法:
a.) - 修改测试加载器,让它每次都重用同一个测试数据库,并在需要时应用迁移
b.) - 从Python文件的__main__流程中运行我的单元测试

选项b在处理sys.path时有点麻烦,选项a虽然可行,但似乎不是Django推荐的做法。

更新:
选项A确实不是一个坏的解决方案,只是需要花费不少精力。这让我觉得大家可能有其他的解决办法。使用SQL Lite可能是一个解决方案,但我猜还有更多其他的选择。

8 个回答

9

我不太喜欢用不同的数据库(比如SQLite)来做测试,所以我的单元测试使用和生产应用相同的数据库——Postgres。

这样一来,创建和销毁数据库就成了运行测试时最慢的步骤。

Django 1.8会通过一个叫做 --keepdb标志 来解决这个问题。

不过我们现在还没有这个功能,所以必须想其他办法。

解决方案1)使用 pytest-django

你可以用这个工具让你的测试在不重新创建数据库的情况下运行。它确实有效。如果你只关心在命令行上运行测试,我推荐这个方法。

在我的情况下,我喜欢使用PyCharm这个开发工具,能够通过右键点击文件或方法来运行测试对我来说是个很大的优点,所以我选择了...

解决方案2)使用TEST_MIRROR技巧。

在你的 settings.py 文件中,配置数据库如下:

if os.getenv('USE_TEST_DB') == '1':
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'NAME': 'mydbtesting',
            'USER': 'mydb',
            'PASSWORD': 'mydb',
            'HOST': 'localhost',
            'PORT': '5432',
            'TEST_MIRROR': 'default',
        }
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'NAME': 'mydb',
            'USER': 'mydb',
            'PASSWORD': 'mydb',
            'HOST': 'localhost',
            'PORT': '5432',
        }
    }

这里,“mydb”是正常运行时使用的数据库,而“mydbtesting”是用来做测试的。

TEST_MIRROR 这个设置其实不是专门为这个目的设计的,但实际上如果你用这种方式配置数据库来运行测试,Django就不会重新创建或销毁数据库,这正是我们想要的。

不过首先我们得用类似下面的命令创建那个数据库:

export USE_TEST_DB=1
./manage.py syncdb --migrate

然后每当你想快速运行测试时,只需把 USE_TEST_DB 环境变量设置为 '1'。要在PyCharm中获得同样的效果,你可以去运行/调试配置,选择默认的Django测试,然后在环境变量中添加 USE_TEST_DB = 1


更新:

示例应用可以在Github上找到: https://github.com/freedomsponsors/www.freedomsponsors.org/blob/099ec1a7a1c404eba287d4c93d58c8cf600b2769

10

在测试时使用内存中的SQLite数据库确实能让事情变得更快。

10

每次都使用相同的测试数据库,并在需要时应用迁移,改进测试加载器。

  1. 我觉得自己写一个测试运行器,把表清空,而不是每次都删除和重新创建数据库,这样做没有问题。这种做法符合Django的风格,因为它解决了一个特定的问题。现在有一个问题单在讨论如何将测试用例分组到测试套件中。一旦这个问题解决了,你就可以把测试用例分组,方便管理。你也可以查看这个问题单里的补丁,看看是否适合你的需求。

  2. 正如Ned建议的那样,你可以使用内存数据库。这在很大程度上取决于你的数据模型和查询是否能够在不同的数据库之间迁移。

  3. 如果你还没有这样做,试着重新组织你的测试用例。根据我的经验,并不是所有的测试类都需要继承django.test.TestCase。找出那些可以继承unittest.TestCase的测试类,这样可以稍微提高速度。

  4. 重新组织数据文件。把常用的数据文件放到一个单独的文件中,并在测试运行之前加载,而不是在每个测试类里面(使用fixtures = [...])。

撰写回答