本节摘要:有些写法让测试"亮假绿灯"——看起来全通过,实际没验证任何该验证的东西。本节盘点六种最高发的反模式,各配一个坏例子和一个补救,目标是让你一读到自己的测试就心里有数。
红灯至少还在说"有事",假绿灯则是明明有风险,页面却显示全绿,等于把警报线剪了。反模式就是这个剪线专业户。它能存在这么久,不是因为大家坏,而是因为"跑起来绿"这个反馈太有迷惑性——测试通过得太容易,人就懒得深究它到底测了什么。下面六种反模式,是测试诊所里复查率最高的回头客。
坏例子:断言内部状态被置成某值,或断言某个私有 helper 被调用。这一改实现,测试就碎。
def test_internal_flag_flips(self): # 反模式:盯内部 self.obj.compute() assert self.obj._done is True # 私有内部,改签名即碎
补法:断言结果或对外接口,包住行为而非暴露内部——换成 assert compute() == EXPECTED。
坏例子:只断言函数跑完没抛异常,或断言一个必然真的条件。形式上有断言,实质上零保险。
def test_returns_something(self): # 反模式:assert something value = func() assert value is not None # 几乎恒真,等于没测
补法:给定明确输入 + 明确预期输出,断言具体值而不是"非空"。
坏例子:两个测试共用同一个模块级列表,一个改一个读,顺序不同结果不同,间歇性闪红。
_doc_months_cache = [] def test_a(): _doc_months_cache.append(1) # 污染 def test_b(): assert len(_doc_months_cache) == 0 # 看运气
补法:每个测试用独立夹具初始化状态,别让用例共享可写的全局容器;确需共享也要用无副作用的 fixture 每次重建。
坏例子:一条用例塞进一堆断言,失败时面对一堵墙看不出哪处错。
def test_whole_order_flow(self): # 反模式:一锅烩 assert order.items assert order.total assert order.stock assert order.tax # 哪里黄了不知道
补法:拆成面向单个行为的原子用例,各自断言一点,配合 2.1 的"原子铁律"。
坏例子:为了把某行变成 100% 覆盖,重复堆同质断言,命中率上去了,可这些样例进化成垃圾——改需求时它们全是噪音。
def test_x_1(): assert fn(1) def test_x_2(): assert fn(2) # 与 test_x_1 几乎重复 def test_x_3(): assert fn(3) # 只为了把参数跑满
补法:用参数化把等价样例合并成一条"画一条边界"的用例,真正把时间花在变种边界和异常路径上。参数化可以用 pytest 的 @pytest.mark.parametrize,代价小信息密度高。
坏例子:单元测试里直接访问公网第三方 API,等于把测试的稳定性和外部服务绑定。偶发超时就闪红,人人学会无视红灯。
补法:坚持 2.2 的依赖注入,把外部调用改成可注入的端口,测试里用替身,真网络验证留给集成层。
| 反模式 | 一句话识别 | 补救一根筋 |
|---|---|---|
| 测实现 | 断言私有/内部调用 | 断言行为与结果 |
| 断言恒真 | 断言"非空""不抛" | 断言具体值 |
| 共享全局态 | 测试间共写一个容器 | 用独立夹具重建 |
| 一锅烩 | 一条带十个断言 | 拆原子用例 |
| 堆样例 | 同质参数刷覆盖 | 参数化 + 边界 |
| 依赖网络 | 单元里真联网 | 注入端口 |
识别反模式的关键不是背这张表,而是建立一种敏感:绿灯亮的时候问一句"它到底拦住了什么真实风险"。答得上来,说明这盏灯有骨头;答不上来,它多半只是占了绿灯的名额。测试的价值不取决于条数,而取决于每一条在发生变化时真能挺身而出拦住一个回归。
反模式有一个反直觉的特点:它们不是孤立存在,而是会传染。团队每新写一条"断言恒真""一锅烩",都会给下一条当榜样——新人照旧测试抄,越抄越像,几轮迭代下来整个套件统一成一个"跑着全绿但我们都不敢动"的大染缸。所以治理反模式别只想"这条怎么修",更要问"这条是从哪条抄来的、源头在谁的评审里"。
对付传染,最省力的两个动作是 Code Review 里刻一条硬规矩和定期的反模式巡诊。前者容易理解:新测试一律过审,审不过的不合。后者则值得给个节奏:每两个月挑一个下午,把新增测试对着 2.5 这张表扫一遍,像扫地一样清一遍——清出来的不是羞辱某个同事,而是证明这套件还能继续被信任。反模式的尽头从来不是"零反例",而是"一出现就有人立刻指出来并修掉"。
还有一种心态层面的误区比技术反模式更难治:把"加了测试"本身当成安全感的来源,却从不检查它到底护不护得住。这类测试写起来很快,过一眼也全绿,但往往是"断言恒真"和"堆样例"的温床——因为它满足的是作者的安心感,而不是系统的回归防护。判断自己有没有掉进去,有一个很直接的问法:上个月有没有哪条测试,在你改坏代码后真的变红救了你一命?如果一条都没有,而那批测试每天都在绿,你很可能正在为安心感写测试,而不是为回归写测试。