bigywater.tech
Flask · 154

Fixture 与测试应用工厂

Flask 的测试该怎么组织 fixture?为什么要用应用工厂?共享一个应用会出什么问题?每个测试新建应用慢不慢?这篇用真实的 pytest 把每种写法的结果跑出来。

3 passed
函数级 fixture 下三个测试的结果,互不影响
assert 1 == 0
session 级应用共享数据库时,test_starts_empty 看到上一个测试留下的数据
约 1.2 ms
每个测试新建一个应用与数据库多花的时间

函数级 fixture

实测 · 本机执行输出
  test_a.py::test_add_one PASSED                                           [ 33%]
  test_a.py::test_starts_empty PASSED                                      [ 66%]
  test_a.py::test_config_override PASSED                                   [100%]
  ============================== 3 passed in 0.02s ===============================

上一篇:test_client() 与真实 HTTP 请求差异;下一篇:测试数据库事务。

fixture 的顺序

实测 · 本机执行输出
          SETUP    F app
          SETUP    F client (fixtures used: app)
          test_a.py::test_add_one (fixtures used: app, client) .
          TEARDOWN F client
          TEARDOWN F app

共享应用的污染

实测 · 本机执行输出
  test_a.py::test_add_one PASSED                                           [ 33%]
  test_a.py::test_starts_empty FAILED                                      [ 66%]
  test_a.py::test_config_override PASSED                                   [100%]
  >       assert client.get("/items").get_json()["count"] == 0
  E       assert 1 == 0
  test_a.py:4: AssertionError
  FAILED test_a.py::test_starts_empty - assert 1 == 0
  ========================= 1 failed, 2 passed in 0.03s ==========================
  (按文件里的顺序,test_add_one 先加了一行,test_starts_empty 看到了它留下的数据)

倒序运行

实测 · 本机执行输出
  test_a.py::test_starts_empty PASSED                                      [ 50%]
  test_a.py::test_add_one PASSED                                           [100%]
  ============================== 2 passed in 0.01s ===============================

创建应用的代价

实测 · 本机执行输出
  函数级 app(每个测试新建)              200 passed in 0.48s | 总耗时(含启动 pytest)0.79 秒
  会话级 app,每个测试清空数据             200 passed in 0.24s | 总耗时(含启动 pytest)0.56 秒

分步演示

Fixture 与测试应用工厂
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。pytest 9.1.1、SQLite 内存库
    • 没有验证:pytest-randomly 等随机顺序插件、pytest-xdist 并行运行时的隔离、使用文件型 SQLite 或 PostgreSQL 时的清理方式(见第 155 篇)、autouse fixture 与 fixture 参数化
    京ICP备2026067502号-1京公网安备11010502063487号