英文:
Is it possible to run a django pytest that has access to multiple databases, without executing the test against each database?
问题 {#heading}
我有一个Django项目,我试图在其中添加第二个数据库,但是我很难让我现有的测试套件配合使用。
我使用了django==3.2.16
,pytest==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 'second' are not allowed in this test. Add 'second' to pytest_django.fixtures._django_db_fixture_helper.<locals>.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=['default', 'second'])
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}
得分: 1
是的,绝对可以在访问两个数据库的情况下运行单个测试,而不必运行两次。当您使用 pytest.mark.django_db(databases=['default', 'second'])
指定多个数据库时,默认情况下 pytest-django 会为每个数据库运行测试。但您可以通过简单的设置来禁用这个行为。
路径 1 {#-1}
您可以通过进行以下更改来实现所需的行为:
-
配置 pytest 不为每个数据库运行测试
在您的
pytest
配置中更新(通常是pytest.ini
或pyproject.toml
)添加以下内容:[pytest] django_find_project = false addopts = --ds=myproject.settings databases = default second multi_db = true
通过设置
multi_db = true
,您允许 Django 为每个使用数据库的测试设置两个数据库,而不会分别运行每个数据库的测试。 -
将
pytestmark
添加到您的测试文件您已经这样做了,但只是为了明确起见:
pytestmark = pytest.mark.django_db(databases=['default', 'second'])
这个标记告诉
pytest-django
应该为测试设置访问default
和second
数据库的权限。
通过这些配置,测试应该只运行一次,但它将可以访问 default
和 second
数据库。
路径 2 {#-2}
-
创建自定义 pytest 标记:
您可以在 pytest 配置中定义自定义标记。将以下内容添加到您的
pytest.ini
中:[pytest] markers = dual_db: 将测试标记为同时使用默认和次要数据库
-
在您的测试中使用这个标记:
使用新的自定义标记来标记需要同时使用两个数据库的测试:
@pytest.mark.dual_db def test_some_function(): # 您的测试代码在这里
-
在
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=['default', 'second'])
, pytest-django will by default run the test for each database. But you can disable this behavior with a simple setting.
Path 1 {#path-1}
You should be able to achieve the desired behavior by making the following changes:
-
Configure pytest to not run tests for each database
Update your
pytest
configuration (usuallypytest.ini
orpyproject.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. -
Add the
pytestmark
to your test fileYou've already done this, but just for clarity:
pytestmark = pytest.mark.django_db(databases=['default', 'second'])
This mark tells
pytest-django
that the test should be set up with access to both thedefault
andsecond
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 {#path-2}
-
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
-
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
-
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 customdual_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="dual_db"): # Attach databases to the test 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'])
This code will customize the test collection phase to check for tests marked with
dual_db
, and then it will set thedatabases
attribute for those tests, allowing them to access both databases. The fixtureenable_db_access_for_dual_db
is automatically used for all tests because of theautouse=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.