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

时间:2016-04-04 12:49:52

标签: django transactions pytest pytest-django

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

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

post_save回调会创建一个Bar记录,OneToOneField指向Foo,但只有在检查Bar记录是否foo_id之后}已经存在(为了避免IntegrityError)。执行此查询即可完成此检查:

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

第一次调用post_save回调时,这是可以的。创建Bar记录,一切正常。但是,第二次,即使这个Bar实例刚刚在之前的Foo保存中创建,因为过滤是一个读操作,它是使用readonly连接执行的,因此already_exists最终包含值False并触发创建新记录,最终会抛出IntegrityError,因为在default连接上执行创建操作时,已经存在记录foo_id

我尝试将DATABASES字典从dev_settings复制到test_settings,但这打破了很多测试。然后我读到了关于override_settings装饰者的信息,并认为这对我的情况来说是完美的。然而,令我惊讶的是,它并没有奏效。似乎在某些时候,当应用程序启动时,DATABASES字典(仅来自test_settings的default字典)被缓存,然后即使我更改setting.DATABASES,新的价值根本不再被访问。

如何正确覆盖某个特定测试的数据库配置?

1 个答案:

答案 0 :(得分:1)

嗯......好吧,如果你只使用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

结帐: Oficial docs