### 【1.53】1.6 测试驱动开发的思想(1.5 分钟) > **口播**:好,同学们,我们进入本章的最后一节——**1.6 测试驱动开发的思想**。先看屏幕上这一页,它是深蓝色的分隔页,橙色的标题写着"测试驱动开发的思想",底下是校园的实景照,右下角是我们广东金融学院的校徽。这一页没有正文,但它承担一个很重要的任务:**给前面五节收口,给下一节翻转顺序。** > 大家回想一下,这一章我们一路走来,讲了测试的必要性、测试的价值、从五个视角认识测试、正向思维和逆向思维、测试与质量保证的分工、测试与开发的并行协作。你会发现,所有这些讨论都指向同一个结论:**测试的地位,必须从"事后把关"提到"事前定义"。** > 那么问题来了,怎么在工程上把这个结论落地呢?靠喊口号吗?不是。靠的是一套人人都能执行的具体做法——**这就是测试驱动开发,英文缩写 TDD。** > 我给大家先透一个底:这一节只有四页,但它是本章的压轴——**前面五节解决的是"测试重不重要",这四页解决的是"测试放在哪里做"。**一个解决认识,一个解决动作。认识不对,动作就白做;动作不落地,认识就是空谈。 > 所以这一节回答的是本章最后那个问题:**测试这件事,到底该在什么时候开始?** **板书**:黑板左侧竖写"需求 → 设计 → 编码 → 测试 → 交付"这条传统顺序;右侧并排写"**1.6 TDD:测试前移、顺序反转**";中间画一个反向箭头,从"测试"指回"需求",箭头旁写"← 提前介入"。 **提问**:TDD 这个名字里,"驱动"两个字最值得我们琢磨。它驱动的到底是什么——是驱动写测试的手,还是驱动写代码的手?换句话说,TDD 最有价值的产出,是那些测试用例本身,还是那条被反转过来的顺序? **预设回答**:第一种回答会说"最值钱的是测试用例,攒下来就是回归测试的资产"——这个回答只对了一半,要肯定它看到了**资产价值**,然后追问:"如果只为了攒用例,那为什么不先写完代码再补用例呢?反正用例最后还是那些用例。"这一问就能把学生推到关键点上。第二种回答会说"最值钱的是那个先后顺序,顺序一变,人做事的思路就变了"——这是我们要的答案,要直接表扬:"对,先写测试,你在心理上就被迫先回答'它应该做什么',而不是'我做了什么',这个心理转向才是 TDD 的命门。"第三种,也会有同学说"不就是把测试提前吗,我提前写就行了"——这是**典型的误读**,要点破:TDD 不只是时间上提前,而是要**先写一个会失败的测试**,看着它红、再让它绿,这个"红—绿"的节奏是有硬性要求的,下一两页我们就讲这个节奏。 **易错点 / 考点**:第一个混淆点,是把 TDD 当成"一种测试技术"——错,**TDD 是一种开发思想、一种设计方法**,它产出的东西是代码,不是测试报告。第二个混淆点,是把 TDD 和"先写测试再写代码"划等号——不够,还要求**测试先行、失败为先、小步快跑、随时可重构**。判断口诀记四个字:**"先、败、小、重"**——先写测试、允许失败、小步进行、随时重构。 **过渡**:名字听过了,位置也说清了,那 TDD 到底怎么个"测试在前、开发在后"?我们翻到下一页,看它那张最经典的循环图。 --- ### 【1.54】测试驱动开发的思想(4.5 分钟) > **口播**:这一页的标题还是"测试驱动开发的思想",正文只有一行字,但这一行字是整节的定盘星——**TDD,test driven development,测试在前,开发在后。**请注意这八个字的分量:它不是"测试和开发同时进行",更不是"开发完再补测试",而是**测试必须排在整个动作序列的最前面。** > 屏幕下半部分画的是经典的 TDD 循环图,我用大白话带大家走一圈,一共八步,一圈一圈地转。 > 起点叫"开始",旁边写"**为新性能写一个测试**"。注意,还没有任何实现代码,先把"我期望它做到什么"写成一条测试。 > 第二步是"**编译**",第三步是"**修订编译错误**"——这一步特别有意思:因为实现还不存在,测试根本编译不过。但大家注意,**这里的报错不是事故,是路标**,它明确告诉你"还差什么"。 > 第四步"**运行测试并发现错误**",第五步"**编写代码**"——只写让这条测试通过所需要的最少代码。第六步"**运行测试并通过**"。第七步"**如果需要就进行重构**"。第八步,箭头回到开头,为下一个新功能再写一个测试,循环往复。 > 我把这个循环概括成四个字,大家记下来:**红、绿、重构。**测试失败是红,测试通过是绿,在绿灯的保护下整理内部结构,就是重构。 > 这里我必须讲透一个最容易讲错的地方:**为什么一定要先看到"红"?**因为如果一条测试你从没见过它失败,你就根本不知道它有没有在验证东西——它可能永远都是绿的,那你写了等于没写。这是 TDD 里最容易被跳过、也最不能跳过的一步。 > 再打个生活化的比方。你去体检,是先定好"血压低于 140 算正常"这个标准,还是等检查完了再回头说"哦,我这份报告看着还行"?当然是先定标准。**TDD 里那条测试,就是你先写下来的体检合格线。**写代码的过程,就是照着这条合格线去做。 > 还有一个词要解释:**重构**。重构不是"重写",它的定义是**在不改变外部行为的前提下,改善代码的内部结构**。那问题来了,谁敢在项目里随手改结构?答案是:**手里有测试网的人才敢。**测试全绿,你改完再跑一遍,绿的就还敢继续走;绿变红了,说明你改坏了,立刻退回。**测试网是重构的安全绳**,这就是"测试驱动"这四个字最实在的收益。 > 最后提醒大家看图上的循环形状——它是一个**闭合的圆**,不是一条直线。直线意味着"做完就完了",圆意味着**每一小圈都留下了一层测试**。转一圈加一个功能,也加一层回归保护。这就是为什么 TDD 出来的项目,越往后越不怕改。 **板书**:画一个顺时针圆圈,标八步:**为新功能写测试 → 编译 → 修编译错误 → 运行测试、发现失败(红)→ 编写代码 → 运行测试、通过(绿)→ 需要则重构 → 回到起点**。圆圈中央写三个大字:**红 · 绿 · 重构**。右下角补一句:**测试 = 先定好的合格线**。 **提问**:TDD 循环的第四步是"运行测试并发现错误"。假如我图省事,跳过这一步,直接写代码、写完了再一次性运行测试,会丢掉什么?请大家具体说出丢掉的到底是什么。 **预设回答**:第一种回答会说"丢掉了一次运行而已,问题不大"。这个回答要当场纠正,因为它把"运行"当成了成本:丢掉的是**对测试本身有效性的验证**——一条从没红过的测试,你没有证据证明它在真的检查东西,它可能只是恒为真的空断言。这时可以追问:"怎么证明一条测试不是摆样子?"答案是:"看它红过。"第二种回答会说"丢掉的是对需求的确认,就是那个编译不过、测试失败的阶段,本来是在逼我想清楚接口长什么样"。这个回答层次很高,要表扬并放大:**TDD 的第一个产物其实不是测试,而是接口设计**,是先想清楚"外面怎么用我",再想"我里面怎么写"。第三种回答会说"省了时间,效率更高"。这个要借势讲代价:省下的是几十秒的运行,赔上的是**没有回归网**——后面每次改代码你都不知道有没有改坏别处,最终会付出更大的返工代价,这跟我们前面讲过的"缺陷发现越迟、成本非线性增长"是同一条规律,只是缩短到了分钟级。 **易错点 / 考点**:三个高频错误。第一,**把"测试在前"理解成"测试文档在前"**——不是,是**可运行的测试代码在前**,必须是能编译、能执行的。第二,**忽略"红的"那一步**,直接写代码再补测试,这不是 TDD,这叫"测试稍后(test after)",考试里经常拿它做干扰项。第三,**把重构和重写混为一谈**——重构**不改变外部行为**,重写会改变;多选题常把"重构=重写"设成错误选项。判断三步法:一看**有没有先写测试**,二看**有没有先看到失败**,三看**有没有在小步循环里积累回归测试**。 **过渡**:TDD 这条循环看着挺简单,但它不是哪个人凭空想出来的。它有明确的出身和出处——下一页我们就去看看,它最早来自哪个开发方法。 --- ### 【1.55】TDD的实践最早来自极限编程(4.5 分钟) > **口播**:这一页的标题是一句结论:**TDD 的实践最早来自极限编程。**极限编程的英文是 Extreme Programming,缩写 XP,它是 1990 年代末敏捷运动中非常有代表性的一种开发方法。TDD 并不是哪本教材坐在办公室里设计出来的,它是**在 XP 的工程实践中先做出来、后被总结成思想的**。这一点很值得让学软件的同学记住:**工程方法往往先有实践,后有理论。** > 屏幕上这张图是课件里给出的 XP 实践流转图,信息量很大,我带着大家一步一步读。 > 我们从最左边开始。起点是"**下一个任务或失败的验收测试**"——注意,一上来就有"失败"两个字,这说明**这个流程是被"还没满足的东西"推着往前走的**,不是被"已经做完的东西"推着走,这个出发点就决定了它的性格。 > 接着箭头分成两层:**简单设计**指向 **CRC 卡**,**复杂问题**也指向 CRC 卡。CRC 就是 Class-Responsibility-Collaborator,类-职责-协作者卡片,是一种很轻量的设计工具。它要说明的是:**不管问题简单还是复杂,设计这一步都不能省,只不过用的工具可以很轻。** > 再往右,从"下一个任务或失败的验收测试"出发,**结对**之后"**创建一个单元测试**"。这里"结对"两个字很关键——**两个人一起干,一个人写、一个人看**,这是 XP 的标志性做法,也正好回应了我们马上要讲的那个问题:**一个人测自己写的东西会有什么毛病。** > 接下来进入中间那个核心循环,我们看它的每一步:**创建单元测试 → 修订代码 → 失败或通过的单元测试 → 不断的集成 → 运行所有单元测试 → 100% 单元测试通过。** > 大家仔细看"修订代码"这个节点,它下面挂着一个词叫"**代码重构**",而且重构旁边标了两条:"**简单代码**"和"**复杂代码**"。这个细节很值得讲:**简单代码就直接改,复杂代码就要靠重构来梳理**——但两条路都通向同一个目标,就是让代码保持可维护。 > 图上还有几条反馈回路,我也点一下:一条向上写"**改变结构、对人员**",一条写"**人员调整**",再往上一条写"**我们需要帮助**"。这几条线说明 XP 不只是技术实践,它同时管人:**结构不合适就调结构,人干不动就调人,搞不定就喊帮助。**这是一种非常坦诚的团队文化。 > 再看最右边:**新的单元测试 → 新功能** 这条线,是"不断集成"时新功能带来的新测试;而最上方一条弧线写着"**运行失败的验收测试**",从"100% 单元测试通过"绕过去,最后落到"**通过验收测试**"。这一段是整个流程的出口——**单元测试全绿只是内部过关,验收测试通过才叫对外交付。** **板书**:左侧竖排写三条主线——**① 任务/失败的验收测试 ② 简单设计与复杂问题 → CRC 卡 ③ 结对 → 创建单元测试**;中间画核心循环:**创建单元测试 ⇄ 修订代码(重构:简单代码/复杂代码)→ 不断集成 → 运行所有单元测试 → 100% 通过**;右侧写出口:**100% 单元测试通过 → 运行失败的验收测试 → 通过验收测试**;最上方批注三条旁线:**人员调整、改变结构、我们需要帮助**。 **提问**:这张流程图的入口写的是"下一个任务**或失败的验收测试**"。为什么一个开发流程,要用"失败"来当起点?换成"下一个要开发的功能",不是更顺耳吗? **预设回答**:第一种回答会说"因为还没有实现,所以测试当然是失败的,这是正常状态"。这个回答已经摸到门了,要顺着往下压一句:"对,但请你再说深一层——**把'失败'摆在起点,等于承认了'不满足'才是工作的常态**,这和你习惯的'我做完一个就勾一个勾'是完全不同的心态。"第二种回答会说"用失败来驱动,能保证每一步都是有目标、可验证的"。这个回答很好,把它升级成一句板书:“**从「还没满足」出发,每一步都有一个可以说清楚的判据。**”第三种是典型的抵触回答:"这不就是先写个必然失败的测试,多此一举吗?"这时候要正面接住:这不是多此一举,恰恰是因为**人天生倾向于保护自己的成果**,先写失败测试,就能把"我写得对不对"这个主观判断,换成一个客观的、机器来判的绿灯/红灯。 **易错点 / 考点**:第一,**极限编程(XP)** 这个名字的写法与归属要记准——考题常问"TDD 最早来自哪种开发方法",答案就是**极限编程(Extreme Programming)**。第二,**CRC 卡(类-职责-协作者)** 是设计工具,容易被误当成编码工具或测试工具。第三,**"100% 单元测试通过"与"通过验收测试"是两个关卡**,不能合并——单元测试是内部的、盯代码的,验收测试是对外的、盯需求的,这个区别在第 56 页还要再往前推一步。 **过渡**:讲到这儿大家应该发现了,TDD 在 XP 里是"一个动作"。后来它长大成了一种"思想",还分出了两个不同的实践层次。下一页,我们就把这三个名字摆在一起说清楚:TDD、UTDD、ATDD。 --- ### 【1.56】TDD与UTDD、ATDD(4.5 分钟) > **口播**:这一页的标题是"**TDD 与 UTDD、ATDD**",页面上还写着一行总结性的话,我先把这行话念清楚:**TDD 成为思想,UTDD 单元测试驱动开发、ATDD 验收测试驱动开发则成为实践。**请大家把这句话记在笔记最上面,因为考试里最爱考的就是这三个词的分工。 > 我们先看屏幕这张图。它是两个同心圆:**里面那个小圆标着 UTDD**,圆里画的是"**单元测试**"和"**代码**"两个方块,两个方块之间是**双向箭头**;**外面那个大圆标着 ATDD**,大圆里面包含了整个小圆。 > 大圆左上方还有一个"**验收指标**"的椭圆,它用箭头指向"**验收测试**";同时它还有一条虚线连到左下角那个**笑脸**图标上。另外还有一条虚线,从笑脸连向"验收测试"的方向。 > 这张图的读法,我教大家一句话:**思想在圆心,实践分两层;内圈管代码,外圈管指标。** > 先说**内圈 UTDD**,Unit Test Driven Development,**单元测试驱动开发**。它的对象是"代码"这个最小的可测单元,做法是先用单元测试把"这个函数应该输入什么、输出什么"钉住,再去把代码写出来,然后让测试变绿。**注意那两个方块之间是双向箭头**——这说明单元测试和代码之间是**相互塑造**的关系:测试约束了代码的行为边界,代码的实现细节又会反过来催生新的测试。 > 再说**外圈 ATDD**,Acceptance Test Driven Development,**验收测试驱动开发**。它站在比单元更高的位置:从"**验收指标**"出发,先把"这个东西要满足什么条件才算交付合格"讲清楚,再把这些指标变成**验收测试**。大家看图上的位置关系——**ATDD 这个大圆把 UTDD 整个装在里面**,这个包含关系非常重要:它说明**验收测试驱动开发并不排斥单元测试,它是把单元测试包在自己的循环里**。业务上的验收条件往下分解,落到代码层面就变成一条条单元测试。 > 最后说**圆心上的 TDD**。为什么说 TDD 是"思想"而不是"实践"?因为思想只有一个内核——**测试先行、以测试来驱动设计与实现**;至于在哪个层次上先写测试,那是实践层面的选择:**在最细的代码层次上先写测试,就是 UTDD;在验收指标的层次上先写测试,就是 ATDD。**两个实践的出发点不同、颗粒度不同、面向的人也不同,但它们共享同一个思想内核。 > 图上那个笑脸,我理解课件想表达的是"**最终结果让使用者满意**"——验收指标是要对着"用户满意"这条线来定的,而不是对着开发自己方便来定的。这个视角一摆正,ATDD 和 UTDD 的分工就清楚了:**UTDD 保证里面是对的,ATDD 保证外面是用户要的。** **板书**:先画一个大圆标 **ATDD(验收测试驱动开发)**,圆内再画一个小圆标 **UTDD(单元测试驱动开发)**;小圆内并排写"**单元测试 ⇄ 代码**";大圆内小圆外写"**验收指标 → 验收测试**",并引一条线连到"**用户满意**";大圆圆心处写一个大字:"**TDD=思想(测试先行)**"。旁边竖排口诀:**思想在圆心,实践分两层;内圈管代码,外圈管指标。** **提问**:假设一个团队说"我们做的是 TDD,但我们只写验收测试、不写单元测试"。按这一页的图来判断,他们的话站得住吗?为什么? **预设回答**:第一种回答会说"站得住,因为 ATDD 把 UTDD 包在里面了,做外圈自然就包含内圈"。这个回答**混淆了"包含"和"替代"**,必须点破:包含关系说的是**范围**,不是说**自动等价**——外圈大并不等于内圈可以被跳过,图里小圆是**实打实画在里面的**,那意味着单元测试这一层仍要存在。第二种回答会说"站不住,ATDD 和 UTDD 是两个不同颗粒度的实践,一个管验收指标、一个管代码细节,验收测试过了不能说明每个单元的逻辑分支都对"。这是标准答案,要追一句把考点夯实:"对,所以这两层是**互补**,不是**互替**。"第三种回答会说"只要产品能用,写不写单元测试无所谓"。这个回答在工程上很危险,正好用它讲代价:没有单元测试,缺陷一旦在集成阶段暴露,**定位成本会成倍上升**,因为你不知道是哪一块坏了——这又是我们第一章反复出现的那条规律:**越晚发现,代价越高。** **易错点 / 考点**:第一,**三个缩写的全称必须能默写**:TDD=Test Driven Development,UTDD=Unit Test Driven Development(单元测试驱动开发),ATDD=Acceptance Test Driven Development(验收测试驱动开发)。第二,**"TDD 是思想,UTDD 和 ATDD 是实践"**这句话是原句,判断题常把"TDD 是一种实践"设成错误选项。第三,**内圆外圆的关系是包含,不是并列,也不是替代**——画图题常考:请把 TDD、UTDD、ATDD 用一个图表示出来,标准答案就是**同心圆**。第四,别把 ATDD 和"验收测试"混为一谈:ATDD 强调**驱动**,也就是**在开发之前就用验收标准来牵引**,它是动作;验收测试是**最终执行的检验**,它是结果。 **过渡**:这一章讲了这么多,从测试的价值讲到 TDD 的思想,最后我要把一个最尖锐的问题摆在大家面前——**让开发人员测自己的产品,到底行不行?TDD 凭什么能解决它?**下一页就是这个问题。 --- ### 【1.57】问题(5.5 分钟) > **口播**:这一页没有别的,页面上就是两个字——"**问题**"。下面两行小字是两个问句:第一,**如果开发人员测试自己的产品,会有哪些障碍?**第二,**TDD 又是如何克服这些障碍的?**这一页是我们第 1 章的思考高点,也是考试、答辩都爱问的地方,我按参考答案的口径,把它一层一层讲透。 > 先说第一个问题,障碍分三层,我们逐层看。 > **第一层,是思维盲区。**人写代码的时候,脑子里已经装着自己那套实现思路。等到回头测试,他会不自觉地**顺着自己的实现思路去测**。这一顺,问题就来了——**他只会去走自己"想到过"的路径,而缺陷恰恰最爱藏在"他没想到"的路径和输入组合里。**说白了,**用你自己的思路去找自己的错,就像一个从来没迷过路的人去画地图,他会把走过的那条路画得很细,把没走过的岔路口漏掉。** > **第二层,是心态偏差。**这一点更隐蔽、也更普遍。人在面对自己亲手做出来的东西时,**天然倾向于证明"我写的是对的"**。这不是道德问题,这是心理机制。所以同样一条失败的测试,别人跑出来会追问"代码哪里错了",自己跑出来第一反应往往是"是不是测试写错了"。**这个倾向会让缺陷被解释掉、被绕过、被改成"设计如此"。**这里我要提醒一句:这才是"开发人员不适合测自己产品"这句话的真正含义——不是能力不够,而是**立场天然不中立**。 > **第三层,是独立性与时间不足。**独立性说的是:**开发和测试最好不是同一个人**,这样评审视角才客观,这在测试组织里叫"独立性原则"。时间不足说的是现实里的排期——开发任务压得最紧的时候,最先被砍掉的往往就是自测和用例设计,因为**它们不直接产出新功能,交付压力一来,第一个被牺牲的就是它们。** > 现在讲第二个问题,**TDD 是怎么把这三层障碍逐一化解的**。它一共给了我们三件武器,正好对着三个障碍。 > **第一件武器,对应思维盲区:先写测试、再写实现。**这个顺序的意义在于,它**逼着你从需求与接口出发**——因为写测试的时候,实现代码一行都还没有,你手里只有"这个功能应该表现成什么样",你只能从外部往里看。**这就把"顺着实现思路测"的路给堵死了。** > **第二件武器,对应独立性与时间不足:测试先行形成自动回归网。**测试是在写功能之前就落下来的,它不会等到交付前才被临时砍掉——**它已经变成了代码库的一部分。**而且这个回归网是自动的,随时能跑,这就把"没时间测"从一个主观借口,变成了一个客观事实:**你的测试覆盖了多少,跑一遍就知道。** > **第三件武器,对应心态偏差:用例就是"可执行规格",减少主观判断。**这是三件武器里最漂亮的一件。原来"这算不算缺陷"是个**主观问题**,靠人吵;现在它变成了**客观问题**——测试通过还是不通过,机器说了算。**规格是可执行的,判断就不再有解释空间。**所以 TDD 不只是让测试更早,它是**把"质量由谁说了算"这个问题,从人手里交给了测试。** > 最后我给大家一句总纲,请记牢:**障碍的根子是"自己人评自己",TDD 的解法是"用先写下来的客观标准去评"。**先把期望写成测试,再把实现写出来——立场问题被顺序解决了,盲区问题被外部视角解决了,时间问题被自动化解决了。 **板书**:左侧写"**障碍三层**":① **思维盲区**——顺着自己的实现思路测,漏掉没想到的路径与组合;② **心态偏差**——倾向证明"我写的是对的";③ **独立性与时间不足**。右侧写"**TDD 三件武器**",用箭头一一对应:① 先写测试再写实现 → **迫使从需求与接口出发**;② 测试先行 → **自动回归网,用例就是可执行规格**;③ 用例即规格 → **减少主观判断**。底部写总纲:**用先写下来的客观标准,去评自己写的东西。** **提问**:三个障碍里,"心态偏差"是最难承认的一个。请你举一个你身上真实发生过的场景——你是怎么在"发现自己的东西不对"的那一瞬间,说服自己"其实这样也行"的? **预设回答**:第一种回答是典型的:"我写作业的时候跑出来结果不对,第一反应是检查输入数据,改了几次数据凑出正确答案,就交了。"这个回答太真实了,要立刻接住并顺势把它变成教学素材:"**注意他刚才做了什么——他改了输入去迁就输出。**这就是心态偏差最标准的表现形式:不是改代码,是改标准。而 TDD 的做法刚好反过来,**标准是事先锁死的,你只能改代码。**"第二种回答会说"我会跟自己说这个边界情况不常见,用户不会这么用"。这也是极好的素材,要追问:"那你凭什么判断用户不会这么用?"答案往往就是"凭感觉"——那就点破:**"凭感觉"正是主观判断,而 TDD 就是把这条"凭感觉"的路用测试堵住。**第三种,也会有同学很硬气地说"我从来都要求自己严格自测,不存在这个偏差"。这时候不要否定他,要用提问引导:"好,那我问你——**你怎么证明你测到了你没想到的地方?**"这个问题他答不上来,因为**你无法用"想到"去覆盖"没想到"**,这恰恰是外部视角和自动化回归网不可替代的原因。 **易错点 / 考点**:第一,**"开发人员不能测自己的产品"这句话的标准理由,是"独立性"与"客观性",不是"水平不行"**——选择题常把"开发人员技术能力不足"设成错误选项。第二,**TDD 克服障碍的机制要能对上号**:先写测试对应"思维盲区",自动回归网对应"时间与独立性",可执行规格对应"心态偏差",这三条是**一一对应**关系,不要交叉。第三,**别忘了"用例即规格"这个提法**——它把主客观判断的边界讲清楚了,简答题里是加分点。判断三步法:**谁在评?评什么?用什么标准评?**——TDD 的答案是"用事先锁死的外部标准评实现"。 **过渡**:这一章从"为什么要测试"一路讲到"TDD 怎么解决自测的障碍",到这里,1.1 到 1.6 的六节内容就全部讲完了。下面我们用一页时间,把这一章的五个理解点收拢起来。 --- ### 【1.58】本章小结(4 分钟) > **口播**:好,这一页是**本章小结**。屏幕上列了五条,我就按这五条,一条一条把第 1 章的要点钉一遍。大家注意,**这五条里的每一条,都对应着我们这一章前面讲过的一整节**,所以我讲的时候,你们可以顺手在心里回放一下那一节的关键句。 > **第一条,理解测试的定义与价值。**测试是什么?按国际标准的说法,是在特定条件下运行系统或组件,观察或记录结果,对它的某个方面做出评价。而测试的价值有四个层面:**全面评估产品质量,拿到客观的质量信息;发现问题、督促问题解决,从而提高质量;持续提供质量反馈、及时揭示质量风险;通过缺陷分析得到缺陷模式,进而预防缺陷。**大家看,从"评估"到"发现"到"反馈"到"预防",这四个词其实是一条**由被动到主动的上升线**——测试走到最高层,不是抓错,而是**让错误不再发生**。 > **第二条,从不同视角认识软件测试。**我们这一章一口气给了五个视角:**质量视角**,测试是对软件质量的全面评估,给出质量信息;**风险视角**,测试是对系统潜在质量风险的评估,而且因为测试是抽样实验、不可能穷尽,风险总是存在的;**经济视角**,测试追求以最小代价获得最高质量,所以**测试成本必须小于缺陷造成的损失,测试才有意义**;还有 **Test Oracle 视角**,输出是否正确需要有判断准则;以及**批判性思维视角**,测试是一个借助观察、经验、反思、推理去收集信息、不断探索的过程。五个视角,其实是五把尺子——**同一件事,换个视角看,你关注的东西完全不同。** > **第三条,正向思维、逆向思维,也会决定测试的行为。**这一条是本章的思想核心。正向思维的代表是 Bill Hetzel 博士,认为测试是**为程序能按预期运行而建立信心的过程**;逆向思维的代表是 Glenford J. Myers,讲得更锋利——**测试是为了证明程序有错,而不是证明程序无错;一个好的测试用例在于它能发现至今未发现的错误;一个成功的测试是发现了至今未发现的错误的测试。**落到行为上就是一个对比:正向思维是"**在设计规定的环境下运行软件的所有功能,直至全部通过**";逆向思维是"**寻找容易犯错误的地方和系统的薄弱环节,试图破坏系统,直至找不出问题**"。**认知决定行为**——你心里假设它是对的,你就在走过场;你心里假设它有错,你才会去挑刺。 > **第四条,理解测试、SQA、质量、开发之间的关系。**SQA 是一项**管理工作**,侧重于对流程的评审和监控,它指导、监督测试的计划与执行;测试是一项**技术性工作**,侧重对产品进行评估和验证,**测试是 SQA 的重要手段之一**,为 SQA 提供质量数据。至于开发,**测试不是开发的下一道工序,两者是并行、协作的关系**——需求、设计、编码的每一步,测试都在场。 > **第五条,理解先进的 TDD 思想。**一句话:**测试在前,开发在后**;它最早来自极限编程的实践,TDD 是思想,UTDD 和 ATDD 是它的两种实践;它靠"先写测试、看到失败、再写实现、然后重构"的循环,把"自己人评自己"这个根子上的障碍给拆掉。 > 最后我给大家留一句本章的总结:**这一章我们学到的不是"怎么测",而是"该在什么时候、以什么心态去测"。**答案是:**越早越好,并且先假设它有错。** **板书**:竖排五条:**① 定义与价值**(评估 → 发现 → 反馈 → 预防);**② 五个视角**(质量 / 风险 / 经济 / Test Oracle / 批判性思维);**③ 正反思维决定行为**(Hetzel 正向 vs. Myers 逆向);**④ 测试—SQA—开发**(管理 vs. 技术;并行而非下道工序);**⑤ TDD**(思想在内,UTDD/ATDD 在外)。黑板最下方写本章一句话:**越早越好,并先假设它有错。** **提问**:这五条里如果要你只留一条带走,你留哪一条?请用一句话说出来,并说明你为什么选它。 **预设回答**:第一种回答会选第①条"定义与价值",理由是"这是基础,后面全都要用它"。这个回答稳妥,要肯定,但可以把它的层次往上推一层:"基础确实重要,不过你再想想——**定义讲的是'测试是什么',心态讲的是'测试的人是谁'**,哪一个更决定成败?"第二种回答选第③条"正向逆向思维",理由是"这一条最反直觉,也最能改变我的做事方式"。这就是我们要的答案,要直接落到板书上:"对,**正向思维是「验证它没问题」,逆向思维是「证明它有问题」,这两种心态下写出来的用例,质量完全不是一个量级。**"第三种回答也会有人说"我选第⑤条 TDD,因为它最具体,能马上用"。这也很好,要顺势补一句:TDD 之所以能落地,正是因为它是把第③条那种逆向心态,**固化成了流程动作**——思想一旦变成动作,就不会因为人的惰性而丢掉。 **易错点 / 考点**:第一,**"测试是 SQA 的重要手段之一"与"测试就是 SQA"要分清**——这是判断题的高频陷阱,两者是**手段与体系**的关系,不是等同关系。第二,**正反思维的代表人物别归错**:正向是 **Bill Hetzel**,逆向是 **Glenford J. Myers**。第三,**经济视角的那句不等式要能默写**:"**测试的成本 < 缺陷造成的损失,测试才有意义**",考试里常把不等号方向设成错误项。第四,**"测试不是开发的下一道工序"**这个判断,是选择题的常客。记忆口诀对第③条尤其有用,四个字:**"假设有错"**——答题时先写这四个字,再展开正反两面的行为差异。 **过渡**:课本上的内容到这儿就总结完了,但学习不能停在"听懂了"。下一页我们留几道题,检验一下这一章是不是真的学进去了。 --- ### 【1.59】提问、思考与练习(5.5 分钟) > **口播**:好,最后一页练习——**提问、思考与练习**。屏幕上有四道题,我要提醒大家一句:**这四道题不是随便凑数的问题,它们的答案基本就是我们这一章所有考点的浓缩,期末和课程设计答辩里都可能以不同形式出现。**所以我不念题、等你们答,我直接把讲法和答案都讲透,你们对照着检查自己的思路。 > **第一题:在日常使用软件的过程中,遇到哪些软件质量问题?**这道题看起来随便,其实考的是"能不能把生活现象对应到缺陷类型"。我按参考答案的口径归类:**闪退、卡顿这类,属于性能与可靠性问题**;**数据丢失或者计算结果错误,属于功能与数据缺陷**;**界面错位、乱码,属于兼容性与易用性问题**;还有一类特别常见——**更新之后老功能失效了,这叫回归缺陷**。请大家留意这四类的名字,**它们就是缺陷分类的入门口径**,也是你们第一次作业要用的标签。我建议你们回去建一个"质量问题随手记",连续记一周,把遇到的每个现象都往这四类里归一次——**归不上类的,那本身就是个值得讨论的例子。** > **第二题:软件测试的正反两方面观点,会如何影响测试工作?**这道题考的是第 1.3.2 节。**正向思维是按规格去验证,"该做的都做到了";反向思维是先假设程序有错,专门挑边界、异常输入和组合去证伪。**这两个思维落到具体工作上,差别非常具体:同样一个"登录"功能,正向思维会写"输入正确账号密码,登录成功";反向思维会写"空密码怎么办、超长输入怎么办、特殊字符怎么办、连续错十次怎么办"。**一个是照着需求清单打勾,一个是照着'哪里可能出错'去挖。**你们做实验的时候,我会明确要求**用例里必须有一半以上是反向用例**,就是这个道理。 > **第三题:软件测试和软件开发的关系是怎样的?如何更好地利用这种关系?**这道题背后是我们讲的"**测试不是开发的下一道工序**"。**它们是并行、协作的关系**:需求的定义、设计、编程、交付,每一段测试都在场。那怎么"更好地利用"呢?答案就是**测试左移**——**在需求和设计阶段,测试就介入进来。**我举两个最实在的例子:需求评审的时候,测试人员专门负责挑**二义性**,一句话如果有两种读法,就当场问清楚;接口还没实现的时候,先把**接口契约和用例冻结下来**,让开发和测试照着同一份规格各干各的。**你越往前介入,你能拦住的问题就越多,返工就越少**——这正是我们第 1.2 节讲过的经济视角。 > **第四题:软件测试和质量保证之间的联系和区别?**这一题最容易答糊,我用一句话把它钉死:**SQA 面向过程,测试面向产品。**SQA 关心的是"**做事的过程合不合规矩**"——规范有没有执行、评审有没有开、度量有没有做、过程有没有在改进;测试关心的是"**做出来的东西对不对**"——验证和确认,把质量信息交出来。**联系在于:测试是 SQA 的重要手段之一,测试跑出来的数据,是 SQA 判断质量、评价过程的客观依据;反过来,SQA 会指导、监督测试的计划和执行,督促测试的结果客观、准确、有效。**大家看,一边是管理体系,一边是技术手段,它们是**相互支撑**的。答题三步法也给大家:**先判属性(管理还是技术)→ 再比侧重(流程还是产品)→ 最后说产出(过程改进的建议,还是质量信息与缺陷数据)。** > 四道题讲完了。屏幕备注里写着"重点学习内容",确实如此——**这四道题,就是这一章的四根支柱。** **板书**:左侧写四个题号;右侧写答题关键词:**① 质量问题四归**(性能与可靠性 / 功能与数据 / 兼容与易用性 / 回归);**② 正反思维**(按规格验证 vs. 专挑边界与异常证伪);**③ 并行协作 + 测试左移**(需求评审挑二义性、接口契约与用例先行);**④ SQA 面向过程 vs. 测试面向产品**(测试是 SQA 的重要手段)。底部写答题三步法:**判属性 → 比侧重 → 说产出**。 **提问**:用第④题的框架考你们一下——如果我发现"这次迭代的缺陷特别多",这个信息应该反馈给谁?是让测试把用例写得更狠一点,还是该去查一查过程和规范哪里出了漏洞? **预设回答**:第一种回答会说"当然先让测试补用例,先把bug都抓出来"。这个回答只做了一半,要肯定它的紧迫性,同时提醒:**缺陷多是一个结果,不一定是一个原因**。追问一句:"如果同一类缺陷反复出现,补用例能解决它第二次出现吗?"——这就是把话题引到 SQA 上。第二种回答会说"两边都要做,短期靠测试补齐覆盖,长期要靠 SQA 去查根因、改过程"。这是最完整的答案,要表扬并帮他说完:"对,**测试解决这一次,SQA 解决下一次**,这正好就是 SQA 要做度量、要做过程改进的原因。"第三种回答会说"缺陷多是开发的问题,跟测试无关"。这个回答要温和地纠偏:**缺陷多也反映测试自己的覆盖与准入把关**——测试有没有在需求阶段就介入、有没有把准入标准卡住。这也正好回应第③题"测试左移"的价值。 **易错点 / 考点**:第一,**第①题的分类口径要能对上号**——"闪退卡顿"对应**性能与可靠性**,不要答成功能缺陷;"更新后旧功能失效"对应**回归缺陷**,不要答成兼容性。第二,**第②题千万别把正反思维答成"好与坏"**——它们不是对错关系,是**两种互补的思维取向**。第三,**第③题的关键词是"并行""协作""测试左移"**,如果只答"测试在开发之后进行",整题零分。第四,**第④题最忌把 SQA 和测试说成"一个东西的两种叫法"**,一定要落到"**过程 vs. 产品**"和"**手段 vs. 体系**"这两组对比上。 **过渡**:四道题答完,这一章的知识就闭环了。学完一门课,光靠课件不够,最后我给大家推荐三本可以继续读下去的书。 --- ### 【1.60】学习资源推荐(3 分钟) > **口播**:这一页是**学习资源推荐**,课件上列了三本。我一本一本说,告诉大家**什么时候该翻哪一本**,这样你们回去才用得上。 > **第一本,《软件测试方法和技术(第 4 版)》,清华大学出版社,2022 年。**这就是我们这门课的教材,也是我们所有术语口径的来源。我提醒一句:**我们课上讲的每一个概念,最后都要回到这本教材的说法上**,尤其是定义、分类、流程这些容易有不同版本表述的地方。所以大家备考的时候,**以这本第 4 版为准**,不要去网上随便找一份说法就背下来。这本书的结构我们也已经在讲第一章的时候带大家看过一遍了——**原理与方法、技术、项目实践这三篇**,正好对应我们这门课"理论加实践"的安排。 > **第二本,《全程软件测试(第 3 版)》,人民邮电出版社,2019 年。**这本书的名字里有两个字最关键——"**全程**"。它讲的不是"测试阶段要做什么",而是**测试怎么贯穿软件开发的整个过程**。你们现在听完第 1 章应该已经有感觉了:我们这一章反复强调的就是**测试左移、测试与开发并行**。等你做课程设计的时候,要写测试计划、要安排测试介入的时机,这本书会给你很完整的参照。我把它定位成**"工程实践视角的补充读物"**。 > **第三本,《致命 Bug 软件缺陷的灾难与启示》,人民邮电出版社,2016 年。**这本书是本**案例集**。前面两本讲"应该怎么做",这一本讲"**没做好会怎样**"。我们课上提到过的那些真实事故——放疗仪致人死亡、处理器浮点除法错误导致大规模召回、航天探测器因为接口没有做集成测试而坠毁,这一类案例在这本书里有更完整的来龙去脉。我要专门说一句:**这一本对你们最有用的场景,是写课程设计报告里的"缺陷影响分析",以及答辩时回答"这个缺陷如果不修会有什么后果"。** > 三本书的分工,我给大家总结成一句:**教材用来定口径,全程测试用来学工程做法,致命 Bug 用来长教训、找案例。** > 最后补一句自己的建议:书不用一次看完。**未来两周,请先把教材第 1 章重读一遍,边读边把你在生活里遇到的软件质量问题记进那个"随手记"里。**这一步做到了,这一章就算真的落地了。 **板书**:竖排三本,每本后面批注用途:**① 《软件测试方法和技术(第 4 版)》(2022,清华)→ 定口径 / 备考依据**;**② 《全程软件测试(第 3 版)》(2019,人邮)→ 全程视角 / 写测试计划的参照**;**③ 《致命 Bug:软件缺陷的灾难与启示》(2016,人邮)→ 案例库 / 缺陷影响分析与答辩素材**。旁边写一句:**教材定口径,全程学做法,Bug 书长教训。** **提问**:这三本书里,如果你现在只能选一本、并且只有两周时间,你会选哪一本来支撑你的课程设计?请连理由一起说。 **预设回答**:第一种回答选第①本教材,理由是"考试以它为准,先把口径对齐"。这个选择很务实,要肯定,同时补一句:"口径对齐之后,别忘了**测试计划是要写给别人看的**,所以第②本迟早要翻。"第二种回答选第②本《全程软件测试》,理由是"课程设计要写测试计划、要安排测试介入时机,这本最直接"。这个判断很到位,要顺势提醒一个风险:"工程做法好学,但**术语口径如果跟教材不一致,答辩时容易被追问**,所以两本要配着看。"第三种回答选第③本《致命 Bug》,理由是"案例最好看、也最好用"。这个回答要鼓励但要纠偏:**案例的价值在于你说得出"启示"**,如果只会复述事故经过,报告里那一段是撑不起来的——**要把事故对应回本章的知识点**,比如"这正是缺少集成测试的代价""这正是经济视角下测试成本小于损失的反面案例",这才叫把案例用活了。 **易错点 / 考点**:这里不是背书的出版社和年份,而是要建立"**什么场景查什么资料**"的判断力。唯一需要精确记忆的是**教材的版本信息**:《软件测试方法和技术(第 4 版)》,清华大学出版社,2022 年——**课程所有概念的最终口径以它为准**。另外提醒两点:一是**别把"全程软件测试"和"软件测试方法和技术"混为一谈**,前者强调测试贯穿全程的工程视角,后者是本课程的系统教材;二是**引用案例时一定要写出启示**,只写事故描述在报告里是不合格的。 **过渡**:这一章我们从"为什么要测试"一直讲到"测试该在什么时候做",中间经过了测试的价值、五个视角、正反思维、SQA 的分工、开发的协作,最后落在 TDD 上。课就上到这里,我最后说两句收尾的话。 --- ### 【1.61】感 谢 聆 听(2 分钟) > **口播**:好,同学们,屏幕上是这一章的最后一页——"**感 谢 聆 听**",底下写着"**广东金融学院 / 周宇文**"。我用这一页的时间,把这一章收个尾。 > 按课件备注里给的口径,我在这里应该说的话是:"**以上是本节课的内容**""**这一章讲到这里**"——这两句话我今天就照着说:**以上是本节课的内容,第 1 章"引论"讲到这里。** > 我们从头回顾一下这一章走的路:我们先问了"**为什么必须做软件测试**",用真实事故说明**缺陷的代价是真实的、而且发现得越晚代价越高**;接着我们从**质量、风险、经济、Test Oracle、批判性思维**五个视角认识了测试;然后讲了**正向思维和逆向思维**——它们不是对错之分,而是两种会直接决定你测试行为的取向;再往下区分了**测试与 SQA**:一个是技术、一个是管理,一个面向产品、一个面向过程;又讲了**测试与开发**:不是上下道工序,而是并行协作,要**测试左移**;最后落在**TDD 思想**上——**测试在前,开发在后**,用先写下来的客观标准,去评自己写的东西。 > 这一章的名字叫"引论",我希望大家现在就记住这一章真正留下的东西,它不是某个定义,而是**一个态度**:**面对软件,不要假设它是对的;作为测试的人,你的价值不在于证明它对,而在于比别人更早发现它不对。** > 课下的两件事我再说一遍:**第一,整理 5 个真实软件缺陷事故,标注类型;第二,用自己的话写出软件测试的定义,并说明它和调试、质量保证的区别。**同时别忘了装好实验环境,完成第一次测试体验——**找出给定小程序里至少两个缺陷,截图记录下来。** > 我们下一次课讲**第 2 章 软件测试的基本概念**,会讲清楚测试的分类、测试的级别、静态测试和动态测试、黑盒白盒灰盒。**带着今天这个"先假设它有错"的心态来,第二章会更好懂。** > 周宇文,广东金融学院。**以上就是本节课的全部内容,谢谢大家,下课。** **板书**:正中间写"**感谢聆听**";右上角竖排写本章六节回放:**1.1 必要性 → 1.2 价值与成本 → 1.3 定义与正反思维 → 1.4 SQA → 1.5 测试与开发 → 1.6 TDD**;左下角写本章一句话态度:**不假设它对,只求更早发现它不对**;右下角写作业与预习:**作业:5 个事故 + 定义辨析;实验:找 2 个缺陷并截图;预习:第 2 章 测试的分类与级别。** **提问**:这一章我们从事故讲到了 TDD。我想请大家用一句话回答:如果只能从这一章带走一样东西,你希望带走的是哪一个判断——是"测试很重要",还是"测试要早",还是"测试要抱着怀疑的心态"?请说你选它的理由。 **预设回答**:第一种回答会说"测试要早,因为成本是越晚越高的,早就是省钱"。这个回答很实在,要肯定它的经济逻辑,同时补一层:"**早**是效率层面的答案,但'早'是为了什么?是为了让发现缺陷的那一秒钟尽量靠前,所以还得配上怀疑的心态,早才有意义。"第二种回答会说"要抱着怀疑的心态,因为正向思维容易走过场"。这是最贴近本章立意的答案,要直接落到板书上:"对,**这一章所有的方法论,最后都要靠这个心态来落地**——心态不对,再好的流程也是走形式。"第三种回答会说"测试很重要"。这个回答没错,但太笼统,可以善意地推一把:"'重要'是一个结论,**这一章我们要的不是结论,而是可执行的态度**——所以请你再往前一步,把'重要'翻译成一个具体动作:比如,从下次写作业开始,先写一份'它应该怎样'的清单,再去实现。"这样一问,学生就能把"重要"落到"测试先行"上,正好和下一章的测试分类衔接起来。 **易错点 / 考点**:一是**本章标题是"引论",考纲里的落点是"理解"层级**,重点在**概念、思维与关系**(测试与 SQA、测试与开发、正反思维),而不是具体技术,答题时不要跑到第 3 章之后的测试方法上去。二是**回顾本章的六节顺序**,别把 **1.4 测试与 SQA** 和 **1.5 测试与开发** 调换——考纲里的顺序就是教材顺序。三是**收尾语的要求**:按课件备注,每课结束时要用"**以上是本节课的内容**""**这一章讲到这里**"这两句话,这是课堂规范的固定动作,答辩或录课时不要漏。 **过渡**:第 1 章到此全部结束。下一节课我们进入**第 2 章 软件测试的基本概念**,从"软件测试分哪几类、有几个测试级别"开始,把今天建立的这些认识落到具体的方法体系上。我们下次课见。