启用enable_db_access_for_all_tests后,如何排除测试类/方法的数据库访问?
好问题!当你全局启用enable_db_access_for_all_tests后,确实所有测试默认都会获得数据库访问权限,但要排除特定测试类/方法其实有几种靠谱的方案,我来给你拆解一下:
方案一:用pytest-django原生的django_no_db fixture
这是最省心的方法,pytest-django本身就提供了django_no_db fixture,专门用来确保测试不接触数据库。你只需要给需要排除的测试类或方法加上这个fixture就行:
标记单个测试方法
def test_without_db(django_no_db): # 这里如果尝试执行数据库操作会直接报错 assert 1 + 1 == 2
标记整个测试类
import pytest @pytest.mark.usefixtures("django_no_db") class TestNoDBRequired: def test_basic_logic(self): # 整个类里的测试都无法访问数据库 assert "hello".upper() == "HELLO" def test_another_non_db_check(self): assert len([1,2,3]) == 3
这个fixture会自动处理数据库连接的关闭,并且拦截任何数据库访问尝试,非常可靠。
方案二:自定义no_django_db标记(更直观)
如果你觉得用fixture标记不够直观,想有一个类似@pytest.mark.django_db的装饰器,可以自己实现一个:
首先在项目根目录的conftest.py里注册自定义标记:
def pytest_configure(config): # 注册自定义标记,方便pytest识别和文档生成 config.addinivalue_line( "markers", "no_django_db: Exclude this test from global database access", )
然后添加一个自动生效的fixture,来拦截标记测试的数据库访问:
import pytest from django.db import connections @pytest.fixture(autouse=True) def block_db_for_marked_tests(request): # 检查当前测试是否被标记了no_django_db if request.node.get_closest_marker("no_django_db"): # 先关闭所有现有数据库连接 for conn in connections.all(): conn.close() # 临时替换连接的execute方法,阻止数据库操作 original_executes = [] for conn in connections.all(): original_executes.append(conn.execute) conn.execute = lambda *args, **kwargs: RuntimeError("Database access is blocked for this test") # 测试运行完后恢复原方法 yield for conn, orig_execute in zip(connections.all(), original_executes): conn.execute = orig_execute else: yield
现在你就可以像用django_db一样用这个标记了:
import pytest @pytest.mark.no_django_db class TestExcludedFromDB: def test_no_db_allowed(self): # 这里如果尝试访问数据库会抛出RuntimeError pass @pytest.mark.no_django_db def test_single_excluded_test(): assert True
方案三:移除全局自动添加的django_db标记
如果想从根源上解决,你可以在conftest.py里用pytest的钩子函数,把标记了no_django_db的测试身上的django_db标记移除(因为全局开启后所有测试都被自动加上了这个标记):
def pytest_collection_modifyitems(items): for item in items: # 如果测试被标记了no_django_db if item.get_closest_marker("no_django_db"): # 找到并移除django_db标记 django_db_marker = item.get_closest_marker("django_db") if django_db_marker: item.removed_markers.append(django_db_marker)
这个方法更底层,相当于让这些测试回到全局开启前的状态,完全没有数据库访问权限。
内容的提问来源于stack exchange,提问作者Daviddd
相关产品推荐
相关产品推荐

