如何在Django测试中覆盖数据库设置?

4

我正在使用Django 1.8(使用pytest),以下是我的配置:

  • 一个由MasterSlaveRouter管理的defaultreadonly数据库,该路由器根据操作是否为读取或写入操作将DB调用定向到其中一个连接。
  • 在我的开发环境中,settings.DATABASES字典中的两个条目具有相同的设置(它们只是使用不同的连接,但数据库是相同的)。
  • 然而,在我的测试环境中,只有一个名为default的数据库。
  • 当保存Foo模型时,我会触发一个post_save信号。
  • 我有一个原子操作(由@transaction.atomic修饰),它修改了一个Foo实例并在其上两次调用.save()。由于没有传递自定义的using参数给装饰器,因此该事务仅在default数据库上处于活动状态。

post_save回调将创建一个Bar记录,该记录具有指向FooOneToOneField,但仅在检查该foo_idBar记录是否已存在后才这样做(以避免IntegrityError)。此检查是通过执行以下查询来完成的:

already_exists = Bar.filter(foo=instance).exists()

第一次调用post_save回调函数时,这是可以的。创建了一个Bar记录,一切都很好。然而,在第二次调用中,尽管在之前的Foo保存中刚刚创建了这样一个Bar实例,但由于过滤是读取操作,所以使用readonly连接执行,因此already_exists最终包含值False,并触发新记录的创建,最终抛出IntegrityError异常,因为当在default连接上执行创建操作时,已经存在具有该foo_id的记录。

我尝试将dev_settings中的DATABASES字典复制到test_settings中,但这破坏了许多测试。然后我了解了override_settings装饰器,并认为它非常适合我的情况。但令我惊讶的是,它不起作用。似乎在某个时候,当应用程序启动时,DATABASES字典(仅带有default的test_settings中的那个)被缓存,即使我更改了setting.DATABASES,新值也不再被访问。

我该如何正确地为一个特定的测试覆盖数据库配置?

1个回答

4

嗯...如果你仅使用pytest,我认为你需要在测试后清理数据库。

现在,要覆盖Django设置,最好的方法是:

from django.test import override_settings
@override_settings(DATABASE_CONFIG=<new_config>)
def test_foo():
    pass

你应该尝试使用 pytest-django:

pytestmark = pytest.mark.django_db

@pytest.mark.django_db
def test_foo():
    pass

当运行测试时,您可以设置create-db参数,强制py.test创建一个新的数据库。如果想要重复使用您的数据库,您可以设置reuse-db。例如:
$ py.test --create-db
$ py.test --reuse-db

结账: 官方文档


警告:我相信文档指出override_settings的机制并不一定适用于某些类型的设置,特别是数据库设置。 - NeilG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接