Is it possible to run a django pytest that has access to multiple databases, without executing the test against each database?

huangapple go评论106阅读模式
英文:

Is it possible to run a django pytest that has access to multiple databases, without executing the test against each database?

问题

我有一个Django项目,我试图在其中添加第二个数据库,但是我很难让我现有的测试套件配合使用。
我使用了django==3.2.16pytest==6.2.2以及pytest-django==4.3.0

我似乎遇到了两个问题:

我看到测试失败,并显示以下错误:

AssertionError: 在此测试中不允许对‘second’进行数据库查询。将‘second’添加到pytest_django.fixtures._django_db_fixture_helper.<locals>.PytestDjangoTestCase.databases,以确保适当的测试隔离并消除此错误。

根据我在问题和文档中找到的信息,我可以像这样做一些配置以使测试具有对第二个数据库的访问权限:

pytestmark = pytest.mark.django_db(databases=['default', 'second'])

在我的测试文件顶部添加了这行代码后,似乎该文件内的所有测试都可以访问‘second’,但是这会导致一个副作用,即该测试会在每个数据库上运行两次。

[gw0] [  2%] 通过 src/app/tests/test_some_stuff.py::test_case_1
[gw0] [  2%] 错误 src/app/tests/test_some_stuff.py::test_case_1

这不是我想要的行为。我需要测试在访问两个数据库的情况下只运行一次,但我似乎找不到现有的解决方案。

我似乎只是在这里打转...如果我只指定一个数据库,测试会因为没有权限访问第二个数据库而失败。如果我同时指定两个,第一个运行会成功,而另一个运行会失败。有没有办法我可以丢弃第二次运行或者做些其他的处理?

英文:

I have a Django project in which I'm trying to add a second database, however I'm having a hard time getting my existing test suite to cooperate.
I'm utilizing django==3.2.16, pytest==6.2.2 along with pytest-django==4.3.0.

I seem to be running into two problems:

I'm seeing tests fail with an error like such

AssertionError: Database queries to &#39;second&#39; are not allowed in this test. Add &#39;second&#39; to pytest_django.fixtures._django_db_fixture_helper.&lt;locals&gt;.PytestDjangoTestCase.databases to ensure proper test isolation and silence this failure.

From what I've found through issues and documentation is that I can do something like this to configure the test to have permission to the second db

pytestmark = pytest.mark.django_db(databases=[&#39;default&#39;, &#39;second&#39;])

By having this at the top of my test file, it seems all the tests within have access to 'second', but this comes with the side effect that the test is now run TWICE, on each of the databases.

[gw0] [  2%] PASSED src/app/tests/test_some_stuff.py::test_case_1
[gw0] [  2%] ERROR src/app/tests/test_some_stuff.py::test_case_1

That is not the behaviour I'm looking for. I need the test to be run once, with access to both databases, and I can't seem to find an existing solution for this.

I just seem to be going in circles here...if I specify just one db the test fails because it doesn't have permission to access the second db. If I specify both, the first run succeeds and the other run fails. Is there a way I can just throw out the second run or something?

答案1

得分: 1

是的,绝对可以在访问两个数据库的情况下运行单个测试,而不必运行两次。当您使用 pytest.mark.django_db(databases=['default', 'second']) 指定多个数据库时,默认情况下 pytest-django 会为每个数据库运行测试。但您可以通过简单的设置来禁用这个行为。

路径 1

您可以通过进行以下更改来实现所需的行为:

  1. 配置 pytest 不为每个数据库运行测试

    在您的 pytest 配置中更新(通常是 pytest.inipyproject.toml)添加以下内容:

    [pytest]
    django_find_project = false
    addopts = --ds=myproject.settings
    databases = default second
    multi_db = true
    

    通过设置 multi_db = true,您允许 Django 为每个使用数据库的测试设置两个数据库,而不会分别运行每个数据库的测试。

  2. pytestmark 添加到您的测试文件

    您已经这样做了,但只是为了明确起见:

    pytestmark = pytest.mark.django_db(databases=['default', 'second'])
    

    这个标记告诉 pytest-django 应该为测试设置访问 defaultsecond 数据库的权限。

通过这些配置,测试应该只运行一次,但它将可以访问 defaultsecond 数据库。

路径 2

  1. 创建自定义 pytest 标记

    您可以在 pytest 配置中定义自定义标记。将以下内容添加到您的 pytest.ini 中:

    [pytest]
    markers =
        dual_db: 将测试标记为同时使用默认和次要数据库
    
  2. 在您的测试中使用这个标记

    使用新的自定义标记来标记需要同时使用两个数据库的测试:

    @pytest.mark.dual_db
    def test_some_function():
        # 您的测试代码在这里
    
  3. conftest.py 中自定义数据库处理

    在测试文件夹的顶层添加一个名为 conftest.py 的文件(如果不存在的话)。使用它来自定义 pytest 对带有自定义 dual_db 标记的测试的行为:

    import pytest
    from pytest_django.fixtures import _django_db_fixture_helper
    
    @pytest.hookimpl(tryfirst=True)
    def pytest_collection_modifyitems(items):
        for item in items:
            if item.get_closest_marker(name="dual_db"):
                # 为测试附加数据库
                setattr(item, 'databases', ['default', 'second'])
    
    @pytest.fixture(autouse=True)
    def enable_db_access_for_dual_db(request):
        marker = request.node.get_closest_marker(name="dual_db")
        if marker:
            _django_db_fixture_helper(request, ['default', 'second'])
    

    这段代码将自定义测试收集阶段以检查标记为 dual_db 的测试,然后为这些测试设置 databases 属性,允许它们访问两个数据库。enable_db_access_for_dual_db 这个 fixture 会自动用于所有测试,因为它使用了 autouse=True 参数,并确保两个数据库都是可访问的。

通过这个设置,您标记为 @pytest.mark.dual_db 的测试应该只运行一次,但它们将可以访问两个数据库。

英文:

Yes, it's definitely possible to run a single test with access to both databases without having it run twice. When you specify multiple databases with pytest.mark.django_db(databases=[&#39;default&#39;, &#39;second&#39;]), pytest-django will by default run the test for each database. But you can disable this behavior with a simple setting.

Path 1

You should be able to achieve the desired behavior by making the following changes:

  1. Configure pytest to not run tests for each database

    Update your pytest configuration (usually pytest.ini or pyproject.toml) with the following:

    [pytest]
    django_find_project = false
    addopts = --ds=myproject.settings
    databases = default second
    multi_db = true
    

    By setting multi_db = true, you allow Django to set up both databases for each test that uses the database, without running the test separately for each database.

  2. Add the pytestmark to your test file

    You've already done this, but just for clarity:

    pytestmark = pytest.mark.django_db(databases=[&#39;default&#39;, &#39;second&#39;])
    

    This mark tells pytest-django that the test should be set up with access to both the default and second databases.

With these configurations, the test should be run only once, but it will have access to both the default and second databases.

Path 2

  1. Create a custom pytest marker:

    You can define a custom marker in your pytest configuration. Add this to your pytest.ini:

    [pytest]
    markers =
        dual_db: mark test as using both default and secondary databases
    
  2. Use the marker in your tests:

    Mark the tests that need to use both databases with the new custom marker:

    @pytest.mark.dual_db
    def test_some_function():
        # your test code here
    
  3. Customize Database Handling in conftest.py:

    Add a file named conftest.py at the top level of your tests folder (if it doesn't already exist). Use it to customize pytest's behavior for tests marked with the custom dual_db marker:

    import pytest
    from pytest_django.fixtures import _django_db_fixture_helper
    
    @pytest.hookimpl(tryfirst=True)
    def pytest_collection_modifyitems(items):
        for item in items:
            if item.get_closest_marker(name=&quot;dual_db&quot;):
                # Attach databases to the test
                setattr(item, &#39;databases&#39;, [&#39;default&#39;, &#39;second&#39;])
    
    @pytest.fixture(autouse=True)
    def enable_db_access_for_dual_db(request):
        marker = request.node.get_closest_marker(name=&quot;dual_db&quot;)
        if marker:
            _django_db_fixture_helper(request, [&#39;default&#39;, &#39;second&#39;])
    

    This code will customize the test collection phase to check for tests marked with dual_db, and then it will set the databases attribute for those tests, allowing them to access both databases. The fixture enable_db_access_for_dual_db is automatically used for all tests because of the autouse=True argument and ensures that both databases are accessible.

With this setup, your tests marked with @pytest.mark.dual_db should only run once, but they'll have access to both databases.

huangapple
  • 本文由 发表于 2023年8月11日 02:55:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76878619.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定