关于使用Pytest优化加速数据库驱动项目测试的困惑
老兄,我完全懂你这种看着测试时间从10分钟涨到15分钟的焦虑——尤其是当你只是想把测试覆盖率补全的时候。咱们来一步步拆解你遇到的问题,看看还有哪些可以优化的地方:
一、优化数据库Fixture的复用策略
你现在的slug fixture每个测试都会创建一次ClientInformation,这会产生大量重复的数据库写入操作,是拖慢速度的关键之一。试试把它改成session级别的fixture,这样整个测试会话只创建一次数据,所有测试共享:
@pytest.fixture(scope="session") def slug(django_db_setup, django_db_blocker): # 手动解锁数据库访问(session级fixture默认无法直接操作DB) with django_db_blocker.unblock(): client = mommy.make('client.ClientInformation', url='test', name='test') return client.url
这里的django_db_setup确保数据库已完成初始化,django_db_blocker用来解除session级别的数据库访问限制,能有效减少重复的DB写入开销。
二、减少测试内的重复数据库操作
看你给出的登录测试例子,里面每次循环都在执行请求,但用户创建和配置的部分可以移到fixture里复用:
@pytest.fixture def staff_user_without_2fa(db): admin = mommy.make('your_app.AdminModel') admin.has_two_factor_authentication = False admin.set_password('password') admin.save() return admin
这样每个用到该用户的测试都不用重复执行set_password和save操作,节省DB写入时间。
三、优化pytest-xdist的进程数配置
你用的-n auto会自动匹配CPU核心数,但进程数并非越多越好——数据库连接池是有限的,过多进程会导致连接耗尽,反而拖慢测试。可以手动尝试不同的进程数,比如-n 4或-n 6,结合你的CI服务器CPU和数据库配置找到最优值。
四、数据库层面的优化
- 切换内存型数据库:如果用的是SQLite,在pytest配置里改成内存数据库,能大幅提升DB操作速度:
注意:SQLite内存数据库是进程隔离的,xdist每个子进程会初始化自己的DB,这时候可以搭配# conftest.py里添加 import pytest from django.conf import settings @pytest.fixture(scope="session", autouse=True) def setup_memory_db(): settings.DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3' settings.DATABASES['default']['NAME'] = ':memory:'--dist=loadfile让每个进程处理特定测试文件,减少重复初始化的开销。 - 减少事务回滚开销:对于只读测试或者无状态测试,使用
transactional_dbfixture替代db,避免每个测试后都回滚事务:def test_some_read_only_operation(transactional_db): # 测试逻辑 pass
五、解决sessionstart中数据库访问的问题
你之前尝试在pytest_sessionstart里创建数据失败,是因为hook函数默认没有数据库访问权限,且@pytest.mark.django_db对hook不生效。正确做法是用django_db_blocker解锁:
def pytest_sessionstart(session): from pytest_django.fixtures import django_db_blocker from client.models import ClientInformation with django_db_blocker.unblock(): # 避免重复创建 if not ClientInformation.objects.filter(url='test').exists(): ClientInformation.objects.create( url='test', name='test', price_per_unit=1.99 )
不过要注意:xdist的子进程不会执行主进程的pytest_sessionstart逻辑,所以如果用多进程测试,还是session级fixture的复用方式更可靠。
六、其他小技巧
- 用
--tb=short或--tb=no减少测试失败时的回溯信息输出,节省IO时间; - 禁用不必要的插件(比如不需要覆盖率时去掉
--cov参数); - 拆分测试为快测(无DB)和慢测(需DB),并行处理快测部分,压缩整体耗时。
总的来说,最见效的优化应该是fixture复用和数据库配置调整,你可以先从把slug改成session级fixture开始尝试,应该能看到明显的时间减少。
备注:内容来源于stack exchange,提问作者ViaTech

