本篇文章阅读时间:10min
读者预期的收获是:
- 认识测试驱动开发
- 非常简单开启你的TDD之旅
- 可以编写自动化测试
- 重构、重新设计旧的代码更加自信
引子
(压抑背景音乐渐入——)
旁白:为何深夜的办公室传来程序员的哀嚎?
为何说好的一刀999,砍下去伤害为0?
为何程序员好基友反目成仇,因代码调用出问题后甩锅大打出手?
当个程序员,好难!(捂着铮亮的脑门)
程序员甲:自从用了TDD,测试驱动开发之后,每天下班早了,BUG变少了,基友不吵了。
程序员乙丙丁:真的吗?有这么神奇吗?!(集体星星眼)
程序员甲:没错,让我来给你们安利吧!
(雪花屏)Beep——
Hi,我是Bruski。开头的段子纯属瞎编,但其中描述的场景:代码不按预期执行、协作的接口不可靠等等,在我们日常工作中其实挺常见的。
原因可能千奇百怪,比如在犯困的午后工作,比如没想清楚就动手等等,而且在过程很糟糕的情况下,输出还没有自动化测试去保证,那线上在跑的程序很可能就是一颗不定时炸弹。
那有没有什么办法能最大程度避免以上情况呢?我会说,不妨试试极限编程(XP)中的优秀实践:测试驱动开发吧!
别问,先感受
那么到底什么是测试驱动开发呢?
别急,先来感受一道小题目,非常简单:FizzBuzz
。
题目模板地址: git clone https://github.com/bruceeewong/tdd-kata.git -b kick-start
题目来源:极客学院-测试驱动开发实战营
FizzBuzz 是一个简单的猜数字游戏。
想象你是个小学5年级的学生,现在还有5分钟就要下课,数学老师带全班同学玩一个小游戏。他会用手指挨个指向每个学生,被指着的学生就要依次报数:
第一个被指着的学生说“1”,第二个被指着的学生说“2”,如果一个学生被指着的时候,应该报的数是3的倍数,那么他就不能说这个数,而是要说“Fizz”。5的倍数也不能被说出来,而是要说“Buzz”。
于是游戏开始了,老师的手指向一个个同学,他们开心地喊
着:“1!”,“2!”,“Fizz!”,“4!”,“Buzz!”……
终于,老师指向了你,时间仿佛静止,你的嘴发干,你的掌心在出汗,你仔细计算,然后终于喊出“Fizz!”。运气不错,你躲过了一劫,游戏继续进行。
为了避免在自己这儿失败,我们想了一个作弊的法子:最好能提前把整个列表打印出来,这样就知道到我这儿的时候该说什么了。
题目要求
写一个程序,打印出从1到100的数字,将其中3的倍数替换成“Fizz”,5的倍数替换成“Buzz”。既能被3整除、又能被5整除的数则替换成“FizzBuzz”。
要求:
- 代码整洁,没有重复代码
- 有单元测试,单元测试覆盖率100%
- 5分钟内完成
题目解析
相信大家应该都能很快地实现题目的要求,不过,关于单元测试部分,大家写的是否轻松呢?接下来我想给大家展示下我的做题思路——用TDD的方式。
测试驱动开发的要义是:测试先行,没有失败的测试,就不允许实现。所以,在动手前我们需要想清楚题目要实现什么,即拆解需求。再回顾下题目要求:
打印出从1到100的数字,将其中3的倍数替换成“Fizz”,5的倍数替换成“Buzz”。既能被3整除、又能被5整除的数则替换成“FizzBuzz”。
打印出1到100的数字?也许会有人开始构思程序:一个for循环,if-else一下,再console.log一下。等等,输出打印到控制台的话,我们怎么写测试验证输出是否正确呢?所以不妨转换下思路,沿着函数的本质:input -> process -> output
来思考,其实我们要做的是:
实现一个函数
输入: 1~100 的数字
处理:
3的倍数替换成"Fizz"
5的倍数替换成“Buzz”
3和5的公倍数(或者15的倍数)替换成“FizzBuzz”
其他数字则转换为字符串
输出:字符串
将需求完全拆解后,对应的测试用例也就信手捻来了,就让我们从最最简单的测试开始,函数就叫fizzbuzz吧,接收参数1,返回字符串“1”。(这种直白的语法就叫断言(Assertion),即把预期输出与实际输出作对比以验证程序是否正确运行)
// 以下语法为Jest.js的测试写法
const fizzbuzz = require("./fizzbuzz");
describe("fizzbuzz", () => {
test("测正常数字返回", () => {
expect(fizzbuzz(1)).toEqual("1");
});
});
执行jest
命令运行测试,结果不出所料地报红了:fizzbuzz is not a function
,毕竟我们此时连函数都没声明。
那我们赶紧定义函数:
function fizzbuzz(num) {
return '1';
}
module.exports = fizzbuzz;
有人会说,函数体返回常量,你在骗自己吗?别急,再执行一下jest命令运行测试:
Yes,测试通过,变为绿色!没错我是硬编码返回了,但这是TDD的第二个重要的要义:只写让测试恰好通过的代码
。好吧我知道留着这样的代码,是不敢入睡的,那就再多加一条测试:
const fizzbuzz = require("./fizzbuzz");
describe("fizzbuzz&#