首页 > 解决方案 > 如何通过 ATOMIC_REQUESTS 在多个数据库上使用 Django 的测试客户端?

问题描述

我在我的 中设置了两个数据库settings.py,每个都有ATOMIC_REQUESTS

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'lolomg',
        'USER': 'lolomg',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '5432',
        'ATOMIC_REQUESTS': True,
    },

    'analytics': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'analytics',
        'USER': 'lolomg',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '5432',
        'ATOMIC_REQUESTS': True,
    },
}

如果我然后编写任何使用 Django 测试客户端的测试,例如

from django.test import TestCase

class TestEndpoints(TestCase):

    def test_books(self):
        self.client.get("api/v1/books")

我将获得如下所示的回溯和异常:

======================================================================
FAIL: test_books (lolomg.books.tests.test_api.EndpointsTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jordi/vcs/lolomg/lolomg/books/tests/test_api.py", line 6, in test_books
    resp = self.client.get(self.url)
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/test/client.py", line 535, in get
    response = super().get(path, data=data, secure=secure, **extra)
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/test/client.py", line 347, in get
    **extra,
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/test/client.py", line 422, in generic
    return self.request(**r)
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/test/client.py", line 503, in request
    raise exc_value
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/lib/python3.7/contextlib.py", line 73, in inner
    with self._recreate_cm():
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/db/transaction.py", line 175, in __enter__
    if not connection.get_autocommit():
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/db/backends/base/base.py", line 379, in get_autocommit
    self.ensure_connection()
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/db/backends/base/base.py", line 217, in ensure_connection
    self.connect()
  File "/home/jordi/vcs/lolomg/lib/python3.7/site-packages/django/test/testcases.py", line 144, in __call__
    raise AssertionError(self.message)

AssertionError: Database connections to 'analytics' are not allowed in this test. Add 'analytics' to lolomg.books.tests.test_api.EndpointsTests.databases to ensure proper test isolation and silence this failure.

通过跟踪该堆栈跟踪,我们看到这种情况正在发生,因为测试客户端analytics由于设置而试图为数据库建立事务ATOMIC_REQUESTS。然而,从 Django 2.2 开始,default现在必须在测试中显式启用与数据库的连接:

https://docs.djangoproject.com/en/3.0/topics/testing/tools/#django.test.TransactionTestCase.databases

这很酷,但我实际上根本不想为这个测试连接到analytics数据库。事实上,只有少数测试需要连接到该数据库,因此将这个数据库用于所有其他测试似乎是错误的。

虽然我可以databases = '__all__'为任何使用测试客户端的测试类做这件事,但这似乎也是错误的。我只是不希望测试客户端尝试对大多数测试的分析数据库做任何事情。

那么我应该怎么做呢?我怎样才能保留每个数据库,但只为实际需要它的测试ATOMIC_REQUESTS启用数据库?analytics

标签: pythondjangounit-testing

解决方案


我对 Django 2 还不是很熟悉,但似乎实现这一点的唯一方法是在测试运行器中禁用分析数据库的ATOMIC_REQUESTS设置,然后在涵盖它的测试中显式地重新打开它。

请参阅https://docs.djangoproject.com/en/2.2/topics/testing/tools/#overriding-settings

我认为这并不理想,因为您将在与代码运行不同的环境中运行测试,但我不确定是否有另一种方法,而不只是为所有测试启用所有数据库。


推荐阅读