跳转到内容

6. 先让检查按预期失败

约 30 分钟 难度:进阶 动手章

第 5 章把「整理正确」裁决成了几条能检查的验收条件。这一章把这些条件变成测试,但顺序是反的:先让测试运行,而且要它失败。

一条测试写完就通过,看起来是好事,其实什么都没证明。它可能只是复述了现有实现的行为,也可能根本没在检查该检查的东西。一条测试值不值得信,先看它在「实现还没做到」的时候会不会红。红不起来的测试,绿了也不算数。

先给解析函数留一个空壳,让测试有对象可跑,但故意什么都不做:

parse-quotes.js
// 还没有实现,先放一个空壳
export function parseQuotes(text) {
// 故意留空:现在跑测试,应该全红
return [];
}

把第 5 章那几条验收条件交给 AI,请它照着写成测试。拿到的测试文件大致是这样,每条测试对应一条验收条件:

parse-quotes.test.js
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { parseQuotes } from './parse-quotes.js';
// 验收条件一:正常行解析出品名、单位和数字价格
test('正常行:解析出品名、单位和数字价格', () => {
const rows = parseQuotes('M6 螺母,个,0.18');
assert.deepEqual(rows[0], { name: 'M6 螺母', unit: '个', price: 0.18, missing: false });
});
// 验收条件二:缺单位的行,单位标记为「缺失」
test('缺单位:单位标记为缺失', () => {
const rows = parseQuotes('不锈钢垫片,,1.20');
assert.equal(rows[0].unit, '缺失');
assert.equal(rows[0].missing, true);
});
// 验收条件三(第5章新裁决):价格为 0 也算缺失
test('价格为 0:价格标记为缺失', () => {
const rows = parseQuotes('赠品,个,0');
assert.equal(rows[0].missing, true);
});

在这两个文件所在目录运行 node --test。空壳返回的是一个空数组,rows[0] 取不到任何东西,三条测试全部失败。这一片红正是这一步要的结果。

不是所有红灯都算数。这一章要的红,是「实现还没做到」这一种;另有几种红是测试自己坏了,得先修掉,不能拿来当验收依据。

看到的红属于哪种怎么处理
断言不满足,或取值时说某个字段是 undefined目标红灯:实现确实还没做到保留,这就是要的红
提示找不到模块、import 路径对不上假红:测试连实现都没加载进来先修好文件名和路径,再重跑
语法错误,或提示缺少某个依赖假红:测试文件或环境本身坏了先修语法、装依赖,再重跑

区分的办法很简单:目标红灯的失败信息里,说的是「期望得到 A,实际得到 B」这类断言不符;假红说的是「文件找不到」「语法错」这类还没跑到断言就崩了的问题。只有断言级别的红,才说明测试真的在盯着行为。

红灯确认无误之后,进入下一章让 AI 写实现之前,先立一条规矩:实现阶段,测试文件只读。

原因是这样:让 AI 去改实现让测试变绿时,如果某条测试怎么都过不了,最省事的做法是把那条测试删掉或改松。一旦这样做,等于为了让结果好看,把尺子改短去迁就实现。测试是第 5 章由人裁决出来的「什么算对」,它不能在实现阶段被实现方随手改动。

真的需要改测试怎么办?停下来,回到第 5 章那一步,由人重新裁决验收条件,改完再解锁。改的是「什么算对」,永远由人决定;不是「怎么让它变绿」,那才交给 AI。

这一章留下两样成对的东西:一份保存下来的红灯输出,和它对应的验收条件。

红灯输出证明每条测试都能在实现缺失时正确地红;验收条件说明每条红各自在守哪一条标准。这两样放在一起,才构成「测试可信」的证据。下一章让 AI 在这套测试的判定下循环,把红一条条变成绿。

这是我裁决好的几条验收条件:______。请照着写成测试,但先不要写实现。我会在实现还是空壳时先跑一遍,确认每条测试都因为「断言不满足」而失败,而不是因为路径或语法出错。测试写好后请不要再改动它。

这一节有错或讲不清? 提个 Issue 直接改文档 请作者催更