而现在我们就绕开TTD这个概念,针对的是这个概念背后真正保证代码质量的关键部分—单元测试。无论是先写单元测试,还是后写单元测试,最终都是通过单元测试来保证代码质量。所以,单元测试才是重中之重,它是程序员的一门必修课,如何编写优秀的单元测试是每个程序员都必须学会的一种技能,甚至可以说是一种生存技能。
一,什么是单元测试
单元测试(Unit Test)是对软件基本单元进行的测试

单元(Unit)是一个应用程序中最小的可测试的部分
面向过程:程序的函数(function)、过程(procedure)
面向对象:类/对象的方法(method)
单元测试的传统定义
是一段代码(通常是一个方法)调用另外一段代码,随后检验一些假设的正确性。如果假设的结果错了,单元测试注定会失败。--来源自“维基百科”(wikipedia)
二,单元测试要点
1,理想情况下,单元测试应该是相互独立的
2,单元测试通常是可以自动化运行
3,开发人员负责编写,用以确保其代码能够符合规范
单元测试好处:
1,自信重构,确保正确性
2,将预期的行为(API)文档化
3,减少调试时间
4,为了可测试性,改进实现(设计和代码)
5,启用代码覆盖率以及其他指标(带来更高的测试覆盖率)
不写单元测试的理由:
1,编写单元测试太花时间了
2,不知道如何编写单元测试
3,运行单元测试的时间太长了
4,测试代码不是我的工作
5,应该测试人员QA负责测试
6,遗留代码,我不清楚代码行为,因此也不能编写单元测试
单元测试不是万能的:
1,单元测试只能测试单元本身的功能,不能解决系统范围的问题,比如集成错误,性能问题
2,带来额外的工作量:一行代码,三五行测试代码
3,只有设计良好的单元测试和规范的开发流程才能保证单元测试发挥期望的作用
三,单元测试框架介绍
许多工具可以帮助执行单元测试。自从TDD在2005年开始流行,框架和工具的市场就有了爆炸性发展。最流行的单元测试工具之一是单元测试框架。这些框架允许定义单元测试代码,控制测试的执行,还提供了一个应用程序来运行测试,并在成功完成测试套件中的每个测试后给出报告。
1,.Net框架:NUnit
2,Java框架:JUnit
3,C++框架:CppUnit、RCUnit、Visual Assert
4,其他主要是xUnit测试框架
测试框架3原则:
1,每个单元测试都必须独立于其他所有单元测试而运行
2,框架应该以单个测试为单位来检测和报告错误
3,应该易于定义要运行哪些单元测试
单元测试采用的测试方法:
白盒测试的测试方法有代码检查法、静态结构分析法、静态质量度量法、逻辑覆盖法、基本路径测试法、域测试、符号测试、Z路径覆盖、程序变异。
其中运用最为广泛的是基本路径测试法。
基本路径测试法是在程序控制流图的基础上,通过分析控制构造的环路复杂性,导出基本可执行路径集合,从而设计测试用例的方法。
设计出的测试用例要保证在测试中程序的语句覆盖100%,条件覆盖100%。
四,单元测试案例
在程序控制流图的基础上,通过分析控制构造的环路复杂性,导出基本可执行路径集合,从而设计测试用例。包括以下4个步骤和一个工具方法:
1.程序的控制流图:描述程序控制流的一种图示方法。
2.程序圈复杂度:McCabe复杂性度量。从程序的环路复杂性可导出程序基本路径集合中的独立路径条数,这是确定程序中每个可执行语句至少执行一次所必须的测试用例数目的上界。
3.导出测试用例:根据圈复杂度和程序结构设计用例数据输入和预期结果。
4.准备测试用例:确保基本路径集中的每一条路径的执行。
工具方法:
图形矩阵:是在基本路径测试中起辅助作用的软件工具,利用它可以实现自动地确定一个基本路径集。
程序的控制流图:描述程序控制流的一种图示方法。
圆圈称为控制流图的一个结点,表示一个或多个无分支的语句或源程序语句
流图只有二种图形符号:
图中的每一个圆称为流图的结点,代表一条或多条语句。
流图中的箭头称为边或连接,代表控制流
任何过程设计都要被翻译成控制流图。
确定基本路径集合(即独立路径集合)。可确定6条独立的路径:
路径1:1-2-9-10-12
路径2:1-2-9-11-12
路径3:1-2-3-9-10-12
路径4:1-2-3-4-5-8-2…
路径5:1-2-3-4-5-6-8-2…
路径6:1-2-3-4-5-6-7-8-2…
单元测试设计准则
1、保持单元测试小巧,快速
2、单元测试应该是全自动/非交互式的
3、让单元测试很容易跑起来
4、对测试进行评估
5、立即修正失败的测试
6、把测试维持在单元级别
7、由简入繁
8、保持测试的独立性
9、Keep tests closes to the class being tested
10、合理的命名测试用例
11、只测试公有接口/方法
12、看成是黑盒
13、看成是白盒
14、先关注执行覆盖率
15、芝麻函数也要测试
16、覆盖边界值
17、提供一个随机值生成器
18、每个特性只测试一次
19、使用显式断言
20、提供反向测试
21、代码设计时谨记测试
22、不要访问预定的外部资源
23、权衡测试成本
24、合理安排测试优先次序
25、为测试失败做好准备
26、写测试用例重现bug
27、了解局限
本文相关经验来自开发大牛“肥某”的分享。