You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于使用Pytest优化加速数据库驱动项目测试的困惑

关于使用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和数据库配置找到最优值。

四、数据库层面的优化

  1. 切换内存型数据库:如果用的是SQLite,在pytest配置里改成内存数据库,能大幅提升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:'
    
    注意:SQLite内存数据库是进程隔离的,xdist每个子进程会初始化自己的DB,这时候可以搭配--dist=loadfile让每个进程处理特定测试文件,减少重复初始化的开销。
  2. 减少事务回滚开销:对于只读测试或者无状态测试,使用transactional_db fixture替代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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 09:20:29