如何在每次测试后重置 Django 测试数据库 ID?

Posted

技术标签:

【中文标题】如何在每次测试后重置 Django 测试数据库 ID?【英文标题】:How can I reset a Django test database id's after each test? 【发布时间】:2019-01-01 09:39:02 【问题描述】:

我有一个 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

在这方面寻求任何帮助,无论是解决问题的新方法还是对我的改进。

TLDR;我需要能够修改 django 单元测试创​​建的数据库中的数据。

【问题讨论】:

这就是你要找的东西:docs.djangoproject.com/en/2.1/topics/testing/advanced/… ? @Ryan w 你能找到解决方案吗?我面临同样的问题:) 谢谢 【参考方案1】:

如果您需要测试来重置主键序列,您可以发出 RawSQL 查询来执行此操作。如何做到这一点在this *** question 中有回答。

如果您使用的是 pytest,则可以使用更简单的选项。我们在所有 Django 项目中都使用 pytest 和 pytest-django,这让测试变得轻而易举。 pytest-django 提供a database fixture that can take a boolean parameter to reset the sequences。像这样使用它:

@pytest.mark.django_db(transaction=True, reset_sequences=True)
def mytest():
    [...]

【讨论】:

用 py.test 很好地发现。我花了一段时间才弄清楚为什么我的测试在从 sqlite 迁移到 posgres 时失败了,添加 reset_sequences 就可以解决问题,因为在 posgres 中,ID 在测试函数之间递增。【参考方案2】:

我通过用 TransactionTestCase 替换 TestCase 并设置 reset_sequences=True 来实现此功能。但是测试运行速度较慢。

from django.test import TransactionTestCase

class ViewTest(TransactionTestCase):
    reset_sequences = True

    def test_view_redirects(self):
       ...

这是doc

【讨论】:

【参考方案3】:

与数据库交互的测试不称为单元测试。它们被称为集成测试。 如果您希望每次都重置数据库,建议使用 django 测试用例:

from django.test import TestCase

【讨论】:

我正在使用TestCase,我应该在问题中展示一个测试示例 但不是来自 django.test Django 测试用例在事务中运行每个测试以提供隔离。所以实际上没有数据保存在db中 就是问题所在,数据没有保存,数据库中没有条目,但是如果没有数据存在,自动增量 id 仍然会增加。这在无测试数据库上的工作方式相同。如果您尝试插入数据但失败,则您插入的下一个数据将具有 id 值,就好像前一个数据已正确插入一样。【参考方案4】:

你正在使用一个fixtures文件——把你想要的任何数据放在那里;然后根据需要在测试中对其进行编辑。

虽然,这更难维护。在我看来 - 有更好的选择可能更像你想要的。

您最好使用factory_boy 之类的东西,并在实例化时使用您提供的虚拟数据生成模型(和相关的外键)。

这样您确切地知道正在测试什么,并且它完全独立于其他一切。好的部分是使用factory_boy,您将拥有一个factories.py 文件,与使用某些固定装置相比,您可以更轻松地保持最新状态。

还有其他选项,例如 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

【讨论】:

以上是关于如何在每次测试后重置 Django 测试数据库 ID?的主要内容,如果未能解决你的问题,请参考以下文章

Spring H2 Test DB 在每次测试之前不会重置

如何在每次测试之前重置 Jest 模拟函数调用计数

如何在每次测试之前重置或清除 ApolloProvider 的 useQuery 模拟?

在 Spring Boot 中,如何在每次测试之前重置指标注册表?

如何在 Django tests.py 文件中的每次测试后重新评估我的 urls.py 文件?

python测试开发django-183.bootstrap-formvalidation重置校验的方法