测试夹具(test fixture,简称 fixture): 跑一条测试之前,把被测对象摆进确定状态、跑完再恢复原状的那套准备与收尾;它管的是前提,本身不判对错。 就像每次开演前照同一张图把布景、灯光、道具摆好:桌子没挪地方,台上的差别才只能来自演员。
英文 fixture 借自机械加工 —— 把工件夹在确定位置,刀具走的路径才有意义;换到软件里,
就是先固定环境,再让被测代码去跑。准备那一步常叫 setup,恢复那一步常叫 teardown;
两者成对出现,才算一次完整的夹具。
一条测试本该只回答一个问题:换了被测代码,结果会不会跟着变。 麻烦在于,结果常常在代码一个字没改的情况下自己变。
三件事的根子是同一个:被测代码每次开跑时站在哪儿,得由你说了算。
预置条件 setup 把状态摆到确定位置 -> 替身 test double 顶掉不受控的外部依赖 -> 测试数据 喂进去的原料 -> 被测代码 这一轮真正要验的东西 -> 断言 assertion 唯一说对与不对的一条 -> 收尾 teardown 恢复原状,别污染下一条
@pytest.fixture(yield 之前是准备、之后是收尾)、unittest 的
setUp / tearDown;Java 里是 JUnit 5 的 @BeforeEach / @AfterEach。场景一:用 pytest 的 fixture,给每条测试一条全新的内存库。
# conftest.py
import sqlite3, pytest
@pytest.fixture
def db():
conn = sqlite3.connect(":memory:") # 准备:每次一条全新的库
conn.executescript(open("schema.sql").read())
yield conn # 交给测试用
conn.close() # 收尾:跑完就丢
# tests/test_orders.py
def test_order_decrements_stock(db):
...
跑 python -m pytest -q:每条用到 db 的测试都自动走一遍「建库 -> 用 -> 关掉」,
不必在每条测试的开头再抄一遍建库代码。
场景二:JUnit 5 用注解把准备与收尾挂上。
class OrderServiceTest {
private InMemoryOrderRepo repo;
@BeforeEach void setUp() { repo = new InMemoryOrderRepo(); }
@AfterEach void tearDown() { repo.close(); }
}
场景三:要验的确实是数据库本身,那就把真的数据库当夹具起起来。
docker run -d --rm -p 5432:5432 -e POSTGRES_PASSWORD=secret postgres:16
Testcontainers 这类库把上面这条命令包成一段代码:测试开跑时拉一个真的 PostgreSQL 容器、 结束就拆掉 —— 用的是真依赖,但起点每次一样,也不碰你本机那份数据。
先问一句:它是前提,还是判据?摆前提、给原料、换依赖都只是前提,只有断言说「对 / 不对」。 前提里再分:整台机器开跑前的状态是夹具,某一个外部依赖是替身, 喂进去的原料是测试数据。
| 名词 | 它是什么 | 和测试夹具什么关系 |
|---|---|---|
| 测试夹具(test fixture) | 开跑前把状态摆进确定位置、跑完再恢复的那套准备与收尾 | 本课主角:它管的是「每次从哪儿出发」 |
| 替身(test double / stub / fake / mock) | 顶掉某一个外部依赖、替它上场应答的那件东西 | 常是夹具准备出来、再注入被测代码的一件东西:两者是嵌套,不是并列 |
| 测试数据(test data) | 喂进被测代码的那批原料本身 | 原料不是机制:喂它的那套装置才叫夹具 |
| 断言(assertion) | 唯一说「对 / 不对」的那一条声明 | 分工在时间上:夹具管开跑前,断言管开跑后 |
| 预置与收尾(setUp / tearDown) | 夹具在代码里的两个挂点 | 两者成对才构成一次夹具:只写一半,等于半个夹具 |
一句话分工:夹具搭台,替身换角,数据当道具,断言才说这出戏演得对不对。
三题自测,每题只有一个正确选项;选完立刻看到解释。
1. 一条测试跑完,把临时数据清掉、让下一条从头开始 —— 这一步叫什么?
对。准备与收尾成对才构成一次夹具;省了收尾,这一条的残留就污染下一条,症状是单跑能过、整套跑挂。
2. 写死返回值、专门顶掉被测代码里那个网络对象的东西,叫什么?
对。替身只负责某一个外部依赖,常常是夹具准备出来之后注入被测代码的一件东西;数据是原料、断言是判据,都不是替身。
3. 同一条测试单跑能过、整套跑就挂,最可能的原因是什么?
对。几条测试共用一份没被恢复的状态,顺序一变结论就变 —— 这正是一次夹具没把前提固定住的表现。