每次测试后如何重置Django测试数据库的id?

14
我有一个Django应用程序,并对其运行一些单元测试。问题不在于某个测试向测试数据库中插入数据,而是在其后的测试中。由于每个测试都没有保存事务,因此来自先前测试的条目不存在,这很正常,尽管自动增量ID仍在增加,就好像仍然有条目插入到数据库中。我需要解决这个问题,因为我正在插入更多数据,其中无法控制所赋予的ID,并且需要能够获取此特定数据进行测试。如果我硬编码代码以抓取对象,那么每次添加新测试时都必须更改代码,这并不理想。
我有多个测试正在运行,但为了简单起见,我将展示两个。
from django.test import TestCase
from app.models import Model

class VersionMerge(TestCase):
   fixtures = ['initial_test_data.json']

   def test_model_test1(self):
       *Insert new data*
       *grab new data in*
       *Check data*

   def test_model_test2(self):
       *Insert new data*
       *grab new data*
       *Check data*

问题出现在test_model_test2中,当我尝试获取新数据时,我必须打印对象以查看id以便能够获取它。
我有一个解决方案,可以在实际数据库上修复它,但不能在测试环境上修复。对于我的情况,我需要连接到docker容器并运行psql命令来重置table_id_seq。
docker exec -t  $CONTAINER_ID psql --dbname=test_database_name -username=user -c "SELECT setval('modelName_appName_id_seq', 2, true)"

这将进入表格并设置上次使用的最后一个id值为2,以使下一个id变为3。但是,每当我尝试从python内部运行该命令时,就会出现以下情况:

cmd = "command above"
os.system(cmd)

当我运行这个程序时,我遇到了以下错误。
sh: 1: docker: not found
sh: 1: docker: not found

我需要你的帮助,要么提供一个新的解决方案,要么改进我的解决方案。

简而言之,我需要能够修改django单元测试创建的数据库中的数据。


3
这是您要找的吗:https://docs.djangoproject.com/en/2.1/topics/testing/advanced/#django.test.TransactionTestCase.reset_sequences? - Cricri
@Ryan w 你能解决这个问题吗?我也遇到了同样的问题 :) 谢谢 - Pooya Kamranjam
相关链接:https://dev59.com/w6Hia4cB1Zd3GeqPWZ90 - djvg
3个回答

6
我通过使用 TransactionTestCase 替换 TestCase 并设置 reset_sequences=True,使其正常工作了。但是测试运行速度变慢了。
from django.test import TransactionTestCase

class ViewTest(TransactionTestCase):
    reset_sequences = True

    def test_view_redirects(self):
       ...

Here's the doc


6
如果您需要测试重置主键序列,可以通过发出RawSQL查询来实现。如何准确地执行此操作在这个StackOverflow问题中已经回答。
如果您正在使用pytest,则有一个更简单的选择。我们在所有Django项目中都使用pytest和pytest-django,它使测试变得轻松。pytest-django提供了一个数据库fixture,可以接受一个布尔参数来重置序列。像这样使用它:
@pytest.mark.django_db(transaction=True, reset_sequences=True)
def mytest():
    [...]

使用py.test真是个好发现。当我从sqlite转移到posgres时,我的测试一直失败,花了我一段时间才弄明白原因。添加reset_sequences解决了问题,因为在posgres中,ID在测试函数之间是递增的。 - Aaron
有一种内置的Django方法可以做到这一点,请参考Pooya的回答。 - undefined

1
你正在使用一个固定数据文件 - 你可以在里面放置任何数据,然后根据需要在测试中进行编辑。
虽然这种方法更难维护。在我看来 - 有更好的选择,可能更符合你的意图。
最好使用类似 factory_boy 这样的东西,并且使用提供的虚拟数据在实例化时生成模型(和相关的外键)。
这样你就知道正在测试什么,而且它完全独立于其他所有内容。好处是,使用 factory_boy 你将有一个 factories.py 文件,你可以更轻松地保持更新,而不是使用某个 fixture。
还有其他选项,如 Mixermodel_mommy,但我只有使用过 factory_boymixer
使用factory_boy,它可能看起来像这样:
   def test_model_test1(self):
       factory.ModelFactory(
       some_specific_attribute='some_specific_value'
       )
       model = Model.objects.all().first()
       # Test against your model

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