- 单元测试是对软件基本组成单元进行的测试
- 这里的基本单元不一定是指一个具体的函数或一个类的方法。在具体实现时,也可能对应的是多个文件中的一组函数
- 单元测试的目的
- 验证单元代码和详细设计文档的一致性
- 跟踪详细设计文档中设计的实现,发现详细设计文档中存在的错误
- 发现在编码过程中引入的错误
- 和设计不符引入的错误
- 虽然和设计相符但由于编码出现疏漏导致错误
- 单元的常见错误
- 单元接口
- 被测单元的输入输出参数在个数、属性、顺序上和详细设计中的描述不一致
- 修改了只做输入用的形式参数,可能会导致数据的错误修改
- 约束条件通过形式参数来传送,导致函数间的控制耦合增大。耦合是指两个实体相互依赖于对方的一个量度,分为以下几种:
- 非直接耦合:两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的
- 数据耦合:一个模块访问另一个模块时,彼此之间是通过简单数据参数(不是控制参数、公共数据结构或外部变量)来交换输入、输出信息的。
- 标记耦合:一组模块通过参数表传递记录信息,就是标记耦合。这个记录是某一数据结构的子结构,而不是简单变量。
- 控制耦合:如果一个模块通过传递开关、标志、名字等控制信息,明显的控制选择另一模块的功能,就是控制耦合
- 外部耦合:一组模块都访问同一简单全局变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合
- 公共耦合:若一组模块都访问同一个公共数据环境,则它们之间的耦合则称为公共耦合。公共的数据环境可以是全局数据结构、共享的通信区、内存的公共覆盖区等
- 内容耦合:
- 一个模块直接访问另一个模块的内部数据
- 一个模块不通过正常入口转到另一模块内部
- 两个模块有一部分程序代码重叠(只可能出现在汇编语言中)
- 一个模块有多个入口
- 局部数据结构
- 单元的局部数据结构是最常见的错误来源。在单元工作过程中,必须测试单元内部的数据能否保持完整性,包括内部数据的内容、形式及相互关系不发生错误
- 单元测试中注意以下几类错误:
- 不正确或不一致的数据类型说明
- 使用尚未复制或尚未初始化的变量
- 错误的初始值或错误的缺省值
- 变量名拼写错误或书写错误
- 不一致的数据类型
- 独立路径
- 设计测试用例查找由于错误的计算、不正确的比较和不正常的控制流而导致的错误
- 常见的错误有:
- 运算的优先次序不正确或误解了运算的有限次序
- 运算的方式错误
- 不同数据类型的比较
- 关系表达式中不正确的变量和比较符
- “差 1 错”。即不正确的多循环或少循环一次
- 错误或不可能的循环终止条件
- 当遇到发散的迭代时不能终止的循环
- 迭代要谨慎使用,调用层次不能过深,迭代过程中如果涉及到大的变量传递需要使用指针来完成
- 可能一直递归下去找不到返回的路径,一直发散下去
- 递归嵌套层次太深,每嵌套调用一次都要消耗堆栈资源,可能会导致递归还未到回归点时,堆栈资源已被耗光
- 不适当的修改了循环变量等
- 出错处理
- 比较完善的单元设计要求能预见出错的条件,并设置适当的出错处理,以便在程序出错时,能对出错程序重新做安排,保证其逻辑上的正确性。
- 出错处理模块经常出现的错误或缺陷有
- 出错的描述难以理解
- 出错的描述不足以对错误定位和确定出错的原因
- 显示的错误与实际的错误不符
- 对错误条件的处理不正确
- 在对错误进行处理之前,错误条件已经引起系统的干预等
- 单元内的出错处理
- 对外部接口来的各种可能的情况进行防范处理
- 对单元内部调用的下层函数的各种可能返回值分别进行处理
- 边界条件
- 在 n 次循环的第 n 次,取最大最小值时容易发生错误
- 特别要注意数据流,控制流中刚好等于、大于、小于确定的比较值时出现错误的可能性
- 单元接口
- 单元测试和集成测试、系统测试的区别
- 测试方法不同
- 单元测试属于白盒测试范畴
- 集成测试属于灰盒测试范畴
- 系统测试属于黑盒测试范畴
- 考察范围不同
- 单元测试主要测试单元内部的数据结构、逻辑控制、异常处理等
- 集成测试主要测试模块之间的接口和接口数据传递关系,以及模块组合后的整体功能
- 系统测试主要测试整个系统相对于需求的符合度
- 评估基准不同
- 单元测试的评估基准主要是逻辑覆盖率
- 集成测试的评估基准主要是接口覆盖率
- 集成测试的评估基准主要是测试用例对需求规格的覆盖率
- 测试方法不同
- 单元测试环境
- 在单元测试时,由于单元本身不是一个独立的程序,一个完整的可运行的软件系统并没有构成,所以需要设计一些辅助测试单元。辅助测试单元有两种:驱动单元和桩单元
- 驱动单元:用来模拟被测单元的上层单元,相当于被测函数的主程序,如 main 函数。它接收测试数据,将相关数据传送给被测单元,启动被测单元,最后再输出实测结果。当被测单元能完成相关功能时,也可以不要驱动单元- 桩单元:用来代替被测单元工作过程中调用的子单元
- 构造单元的测试环境的主要工作有
- 构造最小运行调度系统,即驱动单元,用以模拟被测单元的上一级单元
- 模拟实现单元接口,即单元函数需要调用的其他函数接口,即桩单元
- 模拟生成测试数据和状态,为单元运行准备动态环境
- 驱动单元
- 驱动单元主要完成以下步骤
- 接收测试数据,包含测试用例输入和预期输出
- 把测试用例输出传送给要测试的单元,驱动被测单元运行
- 将被测单元的实际输出和预期输出进行比较,得到测试结果
- 将测试结果输出到指定位置
- 类型:顶层函数、非顶层函数
- 不同接口:函数接口、消息接口、文件接口
- 输出:返回值、消息、内存、变量、文件
- 输出参数数目:1 个、多个
- 驱动单元主要完成以下步骤
- 桩单元
- 桩单元的功能是从测试角度模拟被测单元所调用的其他单元。桩单元需要针对不同的输入,返回不同的期望值,模拟不同的功能
- 类型:自定义函数、系统函数
- 如果被测函数为底层函数,则不需要设计桩单元
- 单元测试策略
- 独立的测试策略
- 方法:不考虑每个模块与其他模块的关系,为每个模块设计桩模块和驱动模块。每个模块进行独立的单元测试
- 优点:该方法是最简单,最容易操作的。可以达到高的结构覆盖率。该方法是纯粹的单元测试
- 缺点:桩函数和驱动函数工作量很大,效率低
- 自顶向下的测试策略
- 方法:先对最顶层的单元进行测试,把顶层所调用的单元做成桩模块。其次对第二层进行测试,使用上面已测试的单元做驱动模块。如此类推直到测试完所有模块
- 优点:可以节省驱动模块的开发工作量,测试效率较高
- 缺点:随着被测单元一个一个被加入,测试过程将变得越来越复杂,并且开发和维护的成本将增加
- 自底向上测试
- 方法:先对模块调用层次图上最底层的模块进行单元测试,模拟调用该模块的模块做驱动模块。然后再对上面一层做单元测试,用下面已被测试过的模块做桩模块。以此类推,直到测试完所有模块
- 优点:可以节省桩模块的开发工作量,测试效率较高
- 缺点:不是纯粹的单元测试,底层函数的测试质量对上层函数的测试将产生很大的影响
- 混合测试
- 自顶向下和自底向上的测试策略综合了集成的概念,随着单元测试的进行,测试覆盖率会越来越难以保证,并且在每个测试之间必须保证相关单元的正确性。独立的测试策略比较独立,覆盖率容易保证,并且可以并行进行,但工作量最大。一般采用混合方法比较好。
- 独立的测试策略
- 单元测试过程
- 单元测试计划阶段:完成单元测试计划
- 计划阶段应当考虑整个单元测试过程的事件表,工作量,任务的划分情况,人员和资源的安排情况,需要的测试工具和测试方法,单元测试结束的标准及验收的标准等,同时还应当考虑可能存在的风险,以及针对这些风险的可能处理方法,并输出《单元测试计划》文档,作为整个单元测试过程的指导
- 单元测试设计阶段:完成单元测试方法
- 设计阶段需要考虑对哪些单元进行测试,被测模块之间的关系以及同其他模块之间的关系,具体测试的策略采用哪一种,如何进行单元测试用例的设计、如何进行单元测试代码设计、采用何种工具等,并输出《单元测试方案》文档,用来指导具体的单元测试操作
- 单元测试实现阶段:完成单元测试用例、单元测试脚本及数据文件
- 实现阶段需要完成单元测试用例设计、脚本的编写,测试驱动模块的编写,测试桩模块的编写工作,输出《单元测试用例》文档、相关测试代码
- 单元测试执行阶段:执行单元测试用例,修改发现的问题并进行回归测试,提交单元测试报告
- 单元测试计划阶段:完成单元测试计划
- 单元测试的原则
- 原则
- 对全新的代码或修改过的代码进行单元测试
- 单元测试根据单元测试计划和方案进行,排除测试的随意性
- 必须保证单元测试计划、单元测试方案、单元测试用例等经过评审
- 当测试用例的测试结果与预期结果不一致时,单元测试的执行人员需如实记录实际的测试结果
- 只有当测试计划中的结束标准达到时,单元测试才能结束
- 对被测试单元需达到一定的代码覆盖率要求
- 从组织、技术、流程三个方面保证做好单元测试
- 组织结构应该保证测试组参加单元测试
- 加强单元测试流程规范性
- 制定单元测试的过程定义
- 单元测试工作产品必须纳入配置管理
- 必须制定覆盖率指标和质量目标来指导和验收单元测试
- 加强详细设计文档评审
- 单元测试者技能的提高
- 加强对单元测试人员的技能培训
- 必须引入工具进行辅助
- 单元测试者加强对被测软件的全面了解
- 单元测试工具介绍
- 代码静态分析工具
- Logiscope(瑞典Telelogic公司)
- McCabe QA(复杂度分析和基本路径分析,美国McCabe & Association公司)
- CodeTest(AMC公司)
- 代码检查工具
- PC-LINT
- CodeChk
- Logiscope
- 测试脚本工具
- TCL(Tool Command Language)
- Python
- Perl(Practical Extraction and Report Language)
- 覆盖率检测工具
- Logiscope
- PureCoverage(Rational公司)
- TrueCoverage(Compuware公司)
- McCabeTest
- CodeTest
- 内存检测工具
- Purify(内存泄漏检查,Rational公司)
- BoundsCheck(内存越界检查,Compuware公司)
- CodeTest公司
- 专为单元测试设计的工具
- RTRT(Rational公司)
- Cantata
- AdaTest
- 开源自动化框架XUnit
- CppUnit
- JUnit
- DUnit
- Nunit
- 代码静态分析工具
- 原则
欢迎扫码关注微信公众号「一朵儿的软件测试之旅」一起学习交流