文章目录
0 背景
测试能提高开发效率和质量,并且能显著降低bug的产生。此篇为《代码整洁之道——程序员的职业素养》的第四篇读书心得,主要讲的是良好的测试习惯和方法,可以用于让我们有意培养自己的测试习惯。
1 测试驱动开发(TDD)
测试驱动开发已经发展十余年,经过历史的验证,已证实TDD确实可以缩短编码周期,提高开发效率,有利于获取极高覆盖率的自动化单元测试。
1. 1 三项法则
- 1, 先编译好“可能不能运行的”单元测试,(因为缺少要测试的对象,可能单元测试无法编译)再开始写产品代码;
- 2,只要有一个单元测试失败了,就不再写其他测试代码了;(而是先集中精力解决这个单元测试的问题)
- 3, 产品代码恰好能让当前失败的单元测试成功通过即可,不需要多写
解释:例如
- 1 ,先开始写单元测试的一小部分,发现缺少需要测试对象的类或者函数,所以单元测试无法编译,
- 2,因此必须编写产品代码,来让测试能够编译成功,产品代码够用即可,
- 3,再回头写单元测试代码
- 4,如此不断循环反复,写一些测试代码,再写一些产品代码,两套代码同步增长互为补充,测试代码匹配于产品代码。(如同抗体匹配于抗原)
1.2 优势
1.2.1 确定性
如果TDD是一行行业的纪律,(就如同手术前医生要洗手一样),那么每天要写上几十个测试,每周要写上成百上千个测试。
在任何时刻,有任何代码修改,都必须运行一下手头的全部测试代码,来确保修改没有破坏任何东西。
1.2.2 缺陷注入率
Maximiliem、George2003、Janzen2005,Nagappan2008等不少报告研究都称TDD可以显著降低缺陷,如下降到原来的1/2、1/5甚至是1/10。
1.2.3 勇气
拥有一套TDD,就相当于拥有一套值得信赖的测试。让你在修改代码的时候,随时知道自己是否破坏了代码,以便自己及时的修改调整,从而让自己可以放手去整理代码。
这样可以让自己的代码变得具有可塑性、更易于理解、修改、扩展,代码整洁了,缺陷也就更少了。
1.2.4 文档
当我们使用第三方合作伙伴的框架时,合作伙伴通常会发一份由文档工程师编写的十分漂亮的手册。作为程序员,也许文档配图很精美,但是我们要想知道如何使用代码,首先应该看的是代码示例,因为代码不会撒谎,
遵循TDD三法则编写的每个单元测试都是一个示例,用代码描述系统的用法。
单元测试可以清楚的描述
- 1,对象的创建方法;
- 2,函数的各种有意义的调用方式;
- 3,函数任何的用法
即单元测试就是文档,它描述了系统设计最底层的设计细节,对于读者来说是最好的底层文档。
1.2.5 设计
因为要先写测试,但是测试的代码却未诞生。这就会强迫我们写出可以单独测试的产品代码(对于一个函数调用了其他函数,为了编写测试,我们就必须找到这个函数的解耦合的方法),从而让我们去考虑什么是好的设计。另外如果先写测试,再写产品代码,可以让我们在测试的深度和捕获错误的灵敏度方面大于先写产品代码再写测试,(因为此时已经知道问题如何让解决,形成可思维定势)
1.2.6 总结
TDD可以提升代码的确定性、给程序员勇气、降低代码的缺陷、优化文档和设计的原则。
1.3 缺点
TDD并不是万能的,如果遵循三项法则写出的测试代码就很糟糕,并且弊大于利,那就不应当选择它。
2 验收测试(沟通、澄清、精确化)
此处的定义为:业务方和开发方合作编写的测试来确定需求是否已经完成。(完成:所有代码写完,所有测试通过,QA和需求方已经认可。)
交流细节是很麻烦的事,往往可能双方都取得了共识,然后带着截然不同的想法离开,这种情况很平常。而编写自动化验收测试足够正式、清晰且结果具有权威性。
2.1 常见问题
- 1,早过精细化(还没启动项目就要精确知道最后得到什么)
- 实际做出来的项目往往和图纸上画的不一样。
- 业务方每次看到运行的程序,又会获得比之前更多的信息,从而反过来影响他们对整个系统的看法,结果就是需求越来越精细,项目也谈不上完工
- 实际做出来的项目往往和图纸上画的不一样。
- 需求是一定会变化的,因此追求精确性是徒劳的,往往只能进行评估。(基于不那么精确的评估)
- 2,迟来的模糊性(直到开发时才把需求具体化)
- 写出来的代码往往不符合实际需求
专业开发人员(包括业务方)必须确定项目中没有任何不确定性。
2.2 流程
2.2.1 自动化
考虑成本,验收测试都应当自动进行。(软件开发周期中有时需求手动测试)而且自动化测试还可以避免开发者误入歧途和节约时间。
2.2.2 谁来写
谁:
- 1,理想情况————业务方、QA
- 2,实际————业务分析员、QA、开发人员
- 3,最坏————开发人员(开发和测试不是同一个人)
职责:
业务分析员:测试“正确路径”————功能是否有业务价值
QA:测试“错误路径”————边界条件、异常、例外情况
开发人员:
- 1,在对应的验收测试完成后开始,运行这些测试,观察失败原因,将验收测试与系统关联起来,然后实现需要的功能,让测试通过。
时间。 - 2,与编写测试的人协商并改进测试是职责,但是绝不被动接受测试。(测试这么要求,我就得这么办)因为自己的职责是协助团队开发出最棒的软件,也就是每个人都要关心错误和疏忽,并协力改正。
2.2.3 什么时候写
遵循“推迟精细化”原则:
- 1,越晚越好,通常是功能执行完成的前几天。
- 2,迭代开始的第一天,就应当准备好最初的几项测试,然后每天完成一些测试,
- 3,到迭代的中间点,所有测试都应当准备完毕,如果这个时候还没有准备所有测试,就应当抽调开发人员,如果经常发生,团队就应该增加BA或QA。
敏捷开发中:只有在选定下一轮或者冲刺所需要的功能之后才编写测试。
2.3 误解
验收测试不是单元测试。
单元测试:
- 程序员写给程序员,
- 是正式的设计文档,
- 描述了底层结构和代码行为。(关心对象:程序员)
- 深入系统内部进行,调用特定类的方法
验收测试:
- 业务方写给业务方,
- 是正式的需求文档,
- 描述的是也业务方认为系统该怎么运行。(关心对象:业务方和程序员)
- 在系统外部,在API或者是UI级别进行
2.4 GUI测试
因为美是主观的,往往GUI会不断变化。要测试GUI就要遵循“单一责任原则”(SRP),把不同原因而变化的元素分开,把根据同一原因变化的元素归类分组。
比较好的方法是
- 1,借助GUI背后的API来测试业务逻辑,
- 2,把GUI和业务逻辑解耦合,在测试GUI时,用测试桩(假逻辑)代替业务逻辑。
2.5 持续集成
务必确保持续集成测试中,单元测试和验收测试每天都能运行好几次。整套测试集成系统应该由源代码管理系统触发,只要有人提交,就开始构建,然后运行所有测试,测试结果会用电子邮件发给团队所有人。
当集成失败了,就应该“立即”停下手里的活,看看如何让测试通过。(如果不严肃对待测试花,就会造成失败的测试被抽离构建系统,从而导致bu*生)
3 测试策略
3.1 QA
- 1,开发小组应该把“QA找不到任何错误”作为努力的目标;
- 2,QA和开发人员紧密相连,是需求规则定义者和特性描述者;
- 3,QA的任务便是和业务人员一起创建自动化验收测试,作为系统真正的需求规范文档,来向开发人员描述系统行为;
- 4,遵循“探索测试”的原则,将系统运行的真实状况反馈给开发人员和业务人员,
3.2 自动化测试金字塔
名称 | 占比 |
---|---|
人工探索测试 | 5% |
系统测试 | 10% GUI |
集成测试 | 20% API |
组件测试 | 50% APi |
单元测试 | 100% XUnit |
3.2.1 单元测试
- 1,先写测试,再写编写产品代码;
- 2,真实覆盖率到90%以上;
- 3,单元测试将作为持续集成的一部分运行,来确保代码意图没有遭到破坏;
- 4,使用与系统开发相同的语言编写测试,来在最底层上定义系统,供自己使用
3.2.2 组件测试
- 1,验收测试的一种;
- 2,封装了业务规则;
- 3,由业务人员和QA编写,开发人员提供辅助;(可以在Fitnesse、JBhave或Cucumber等组件测试环境编写或者使用Slenium或Watir之类的GUI测试环境);
- 4,差不多覆盖系统一半,测试是否满足项目需求;
- 5,向组件中输入数据,测实际输出是否满足预期输出;
- 6,使用合适的模拟对象和测试辅助,与系统的其他组件解耦。
3.2.3 集成测试
- 1,对组件较多的较大型测试才有意义;
- 2,测试组件之间是否可以正常通信,系统架构侧面是否正确;
- 3,由系统架构师和设计师编写;
3.3.3 系统测试
- 1,最终的集成测试,不测业务规则,测系统是否已正确组装完毕,组件之间是否正确交互;
- 2,在系统层面进行性能测试和吞吐率测试;
- 3,目的是确保正确的系统构造,而不是正确的系统行为。
3.3.4 人工介入
- 1,验证预期行为的时候,探索系统预测之外的行为;
- 2,需要使用人的创新能力;
- 3,确保在人工操作下表现良好,同时富有创造性的找出尽可能多的“古怪之处”
4 总结
开发团队应该要和QA紧密协作,创建由单元测试、组件测试、集成测试、系统测试和探索性测试构成的测试体系,并尽可能频繁的运行这些测试,得到反馈并修改。