4 构筑测试体系
4.1 自测试代码的价值
- 程序员编写代码的时间仅占所有时间中很少的一部分,但是花费在调试上的时间是最多的。修复bug通常是比较快的,但找出bug所在却是一场噩梦
 - 确保所有测试都是完全自动化,让他们检查自己的测试结果
 - 一套测试就是一个强大的bug侦探器,能够大大缩减查找bug所需的时间
 
4.2 测试代码示例
4.3 第一个测试
- 总是确保测试不该通过时,会产生失败
 - 频繁地运行测试,对于你正在处理的代码与其对应的测试至少每隔几分钟就要运行一次,每天至少运行一次所有的测试
 
4.4 再添加一个测试
- 编写为臻完善的测试并经常运行,好过对完美测试的无尽等待
 - 保持每个测试用例独立性,避免产生共享对象。因为测试之间会通过共享产生交互,而测试的结果就会受测试运行次序的影响,导致测试结果的不确定性
 - 例子
 
describe('province', () => {
     const shanghai = new Province('shanghai');
     it('shortfall', () => {
         expect(shanghai.shortfall).equal(5)
     })
 })
describe('province', () => {
     let shanghai = null;
     beforeEach(() => {
         shanghai = new Province('shanghai');
     })
     it('shortfall', () => {
         expect(shanghai.shortfall).equal(5)
     })
 })
4.5 修改测试夹具
4.6 探测边界条件
- 考虑可能出错的边界条件,把测试火力集中在那儿
 - 不要因为测试无法捕捉所有的bug就不写测试,因为测试的确可以捕捉到大多数bug
 - 任何测试都不能证明一个程序没有bug
 - 当测试数量达到一定程度后,继续增加测试代理的边际效用会递减
 - 应该把测试集中在可能出错的地方,观察代码,看哪儿变得复杂、哪些地方可能出错
 
4.7 测试远不止如此
- 一个架构的好坏,很大程度上要取决于它的可测试性,这是一个好的行业趋势
 - 每当收到bug报告,请先写一个单元测试来暴露这个bug
 - 一个测试集是否够好,最好的衡量标准其实是主观的,试问自己:如果有人在代码里引入了一个缺陷,自己有多大的自信它能被测试集发现
 
6.第一组重构
6.1 提炼函数