软件测试理论—单元测试
课程内容 1.为什么做单元测试 2.单元测试的概念和内容 3.如何做单元测试 4.单元测试的难点和对策
程序员的难题 怎么办? 开发的模块出现问题,很难定位,已经熬了几个通宵了!!! 刚改正了一个BUG,过没几天,又发现了新问题!!! 程序总在出问题,联调了几个月,还是问题不断!!! 怎么办?
高质量的程序 高质量的程序取决于以下几个方面: 1. 高质量的设计 2. 规范的编码 3. 有效的测试
程序员的职责 我是程序员,除了编码我还需做些什么?
程序员的职责 传统的开发观念? 1.开发人员的任务是完成编程,让系统正确运行起来。 2.程序调试通过任务就完成了。 3.自信自己的程序不会出错。 实际: 1.开发人员的任务是完成程序,直到交付和维护。 2.人的失误是不可避免的,无论多小心,都会有错误。
小插曲 你以前做过程序开发工作么? 你是怎样自测的?效果如何?
现实中的发现 编码阶段引入的缺陷远远多于其它阶段 系统测试发现的缺陷大多数是编码缺陷 测试版本频繁,测试和项目进度被无休止的拖延。 Why?
开发部的压力 现状:一个承担多个角色的团队 设计 编码 测试 参与或部分参与高层设计; 承担低层设计; 程序实现; 承担低层测试;
开发部的测试 效果不好:为什么? 没有时间测试 不知道怎样测试 不好组织 缺乏方法和工具 这种情况下,往往把单元测试的任务堆积到系统测试阶段
问题 如果把单元测试的任务堆积到系统测试阶段,将会怎样? 大量的故障堆积在项目中后期:项目后10%的工作,占用了项目90%的时间。 故障难以定位 故障飘忽不定 开发、测试人员疲于奔命
软件缺陷的修复费用
单元测试(why) 最高的成本收益比 减少联调和后续测试的时间 BUG更容易定位 更有信心去修改老代码
业界平均水平 商业软件 单元测试工作量/总工作量= 8.3% 编码工作量/总工作量= 16.6% 军工软件 单元测试工作量/总工作量= 10.1% 编码工作量/总工作量= 18.1%
业界标杆 单元测试(25%) 审查评审(20%) 设计(17%) 编码(14%) 需求(7%) 系统测试(4%) 计划和跟踪(4%) 发布后缺陷0.06 Defects/KLOC 单元测试发现的缺陷密度:31 defects/KLOC
主题内容 1.为什么做单元测试 2.单元测试的概念和内容 3.如何做单元测试 4.单元测试的难点和对策
单元是什么? (IEEE)软件单元 指软件设计说明中一个可独立测试的元素,是程序中一个逻辑上独立的部分,它不能再分解为其他软件成分。 (实践中)软件单元 指软件源代码中单个的函数,源文件或类。
单元测试是什么? 单元测试,对单个的软件单元或者一组相关的软件单元所进行的测试,是代码级的测试。 Unit:函数,源代码文件,类 把测试比作是清洗一台机器: 系统测试就是清除机器外面的尘土。 集成测试就是保证机器各个部件的接头处干净。 单元测试就是清洗各个零件的内部。
单元测试 应用 对象 输入 潜在错误
单元测试 That is easy! 测试一个类
单元测试原则 应该尽早地进行软件单元测试。 应该保证单元测试的可重复性。 尽可能地采用测试自动化的手段来支持单元测 试活动。
单元测试内容 单元功能测试 单元接口测试 单元局部数据结构测试 单元中重要的执行路径测试 单元的各类错误处理路径测试 单元边界条件测试
单元测试内容 面向单元的白盒测试 (单元覆盖率测试) 设计评审 狭义的 单元测试 内容 面向单元的黑盒测试 (单元功能测试) 代码走查 开发测试 单元测试 内存和运行错误分析 (内存泄漏、 越界,异常) 集成测试 代码运行性能profile (函数效率和 瓶颈分析)
单元测试(who) 单元测试可以是开发者本人执行,也 可以是独立的专业测试人员执行。 两者各有优势。 建议开发人员必须完整地做单元测 试,同时测试人员针对重点模块实施 独立的单元测试。
主题内容 1.为什么做单元测试 2.单元测试的概念和内容 3.如何做单元测试 4.单元测试的难点和对策
单元测试过程 单元测试过程包括8个活动: 确定单元测试计划 确定待测特性 制订单元测试规程 设计测试套件 构建测试套件 执行测试套件 检查终止条件 评估测试结果
确定单元测试计划 确定单元测试范围 尽可能争取完全地覆盖(原则上应该做到完全覆盖) 参考:通常以下情况必须安排单元测试: a)新模块 b)新增代码比例超过20% c)核心模块
确定单元测试计划 单元测试充分性要求 例如:语句行覆盖率=100%;分支覆盖率〉85% 测试覆盖率要求是测试充分性的一个方面,除此之外,在单元测试中还应考虑每个软件特性的测试覆盖,如函数性能。
确定单元测试计划 确定终止条件 确定单元测试过程的正常终止条件。该终止条件应该包括了对测试充分性要求的满足。(100%代码行覆盖,85%分支覆盖) 识别可能造成单元测试过程异常终止的条件(如发现重大的设计错误、到达进度期限等)。
确定单元测试计划 确定单元测试资源 估算进行测试活动所需的资源。应考虑测试人员、硬件、通信或系统软件、测试工具和其它资源。识别需要进行准备或申请的资源(如定制的测试工具),并做出相应的安排。 指明总体进度计划 基于资源和项目计划等方面的要求,确定单元测试活动的总体进度计划。
确定待测特性 研究待测特性要从研究单元的需求开始 功能需求、非功能需求(如性能或设计约束等)、与待测单元相关的任何使用或操作过程 单元的状态识别 针对状态机测试 单元的数据特性识别 单元的输入输出数据分析 以上研究分析对于制定单元测试方案和指导测试用例的设计很重要 待测特性分析过程中还有可能发现单元需求上的缺陷。
制定单元测试规程 输入 单元测试计划、待测特性分析结果、项目总体进度计划 识别可重用技术(待查) 通过待测特性分析,可从用例库中识别出可以重用的测试用例和测试规程,以减少重复工作。 资源 详细列举单元测试所需资源,包括人员、设备、工具、环境等, 进度计划 详细的进度计划,包括风险分析和应对措施 规程评审
设计测试套件 测试套件 测试用例、脚本、驱动、桩、测试数据 测试规程和测试用例的开发 目前测试规程和测试用例是合一的。开发过程中在重用的基础上新增和修改。结合待测单元特性分析,充分考虑测试用例的覆盖率。 �测试工具的设计 自研测试工具的设计要充分考虑可重用性,不同项目间通用性一般较小,统一项目不同版本间一定要具备通用性。 测试规程/用例的评审
单元测试数据 单元测试设计中,测试数据的设计是很关键的,同样的测试规程,不 同的测试数据,可能会达到不同的测试结果。 a) 正常数据:在测试中所用的正常数据的量是最大的,而且也是最关 键的。少量的测试数据不能完全覆盖需求,但我们要从中提取出一 些具有高度代表性的数据作为测试数据,以减少测试时间。 b) 边缘数据:边缘测试是界于正常数据和错误数据之间的一种数据。 它可以针对某一种编程语言、编程环境或特定的数据库而专门设 定。边缘数据要靠测试人员的丰富经验来制定。 c) 错误数据:显而易见,错误数据就是编写与程序输入规范不符的数 据从而检测输入筛选、错误处理等程序的分支。
构建测试套件 测试数据的准备 测试工具的开发/调试 构建测试环境
执行测试套件 运行测试 确定测试结果,处理测试过程中的异常 对每个测试用例,确定单元是否通过测试。对异常进行分析,并根据情况处理: 情况1:测试用例或测试数据的问题。修正并重新运行。 情况2:测试规程执行的问题。重新运行。 情况3:测试环境的问题。纠正测试环境并重新运行;或者异常终止测试,并汇报记录异常终止原因。 情况4:单元实现中的故障。纠正单元的故障,并运行所有的测试;或者异常终止测试,并汇报记录异常终止原因。 情况5:单元设计中的故障。纠正单元设计和实现中的故障,必要时修改测试设计和测试数据,并重新运行所有的测试。
检查终止条件 测试充分性检查 检查是否达到覆盖率要求,包括测试用例执行/通过覆盖率和被测单元代码/分支覆盖率。以及其它测试充分性要求。 异常终止条件检查 补充测试套件 以上条件不满足时,则需要补充测试套件,继续进行测试。
评估测试结果 按照单元测试报告模块出具单元测试报告 如有必要对单元测试报告进行评审 将所有测试相关工作产品纳入配置管理
主题内容 1.为什么做单元测试 2.单元测试的概念和内容 3.单元测试的方法、技术与工具 4.如何做单元测试 5.单元测试的难点和对策
参见的单元测试的难点 没有时间做单元测试 单元测试责任人不清楚 测试代码难以管理 覆盖率难以手工统计 故障报告形式 驱动和桩编写困难(可测试性)
对策:没有时间做单元测试 单元测试计划在项目计划应该有体现。 编写代码之前或同时,先设计测试用例。每个软件单元应该有什么功能?是否每个功能都有测试用例来验证它?
对策:单元测试责任人不清楚 强调单元测试必须由类包的设计者负责编写,因为只有这样,测试才能保证对象的运行时态行为符合需求。 让测试人员或第三方人员编写测试用例,将花费更多的工作量。(20 >> 1) 执行测试用例可以让测试人员或自动构造系统。
对策:测试代码难以管理 采用测试工具管理测试代码 如XUnit、C++Test、RTRT 配置管理中建立配置项 如,不同模块的一组代码,建立相应测试代码目录和配置 项
对策:覆盖率难以手工统计 利用各种工具 PureCoverage (C/C++/Java/.Net,Windows/UNIX) RTRT(C/C++/Java/Ada,嵌入式系统) C++Test(C/C++,Windows/UNIX) Discover(Delphi,Windows)
对策:故障报告形式 各种工具一般都会生成测试报告 XUnit 测试用例执行报告 RTRT、C++Test各种综合报告(测试用例执行结果、测 试用例覆盖率、内存检查和性能)
对策:驱动和桩编写困难(可测试性差) 通常情形下,测试驱动难以编写,测试难以进行由以下几方面原因导致: 1、被测试对象需要传入的参数过多。 2、内部的逻辑判断过多(内部牵扯复杂)。 3、和界面显示部分交互过于频繁(耦合性太强)。 4、被测对象过多的调用了其他类或方法。 5、需要构造的作为参数的对象本身过于复杂
解决:提高可测试性 1、首先最重要的是坚持测试驱动设计(测试先于设计)的方法。 优先编写测试代码。这是标准的XP 方法。这不是说您应该一次性编写全部测试代码后,再一次性全部实现。对一些单元接口,编写一些测试代码,实现它们,再编写一些测试代码,再实现它们等等是个更好的办法。设计以这种方式得以进展;在实现阶段捕捉错误并在下一组测试中改正它。 2、功能分解 类:把功能分解到细粒度,提倡小类。 方法:尽量做到每个操作对应一个方法,使方法小型化。 功能分解促进:提高重用性,降低耦合度 3、分层原则。 对于显示部分(GUI),尽量做到显示与控制分离。把代码移到GUI 视图的外面。然后各种GUI 动作就能成了模型上的简单方法调用。这样,对GUI 测试者来说,通过方法调用测试功能比间接地测试功能容易的多。另一个好处是它使修改程序功能而不影响视图变的更容易。
解决:提高可测试性 4、抽象 我们可以想出各种各样的办法来降低耦合程度,但是归纳 起来,不外乎增加抽象的层次来隔离不同的类,这个抽 象层次可以是具体的类,也可以是接口。 GOF的23种设计模式,没有一种模式的思路不是从增加抽 象层次入手来解决问题的 5、对于可能要作为参数的复杂类,可以做一个接口,用接 口说明外部程序组件使得我们可以容易地在测试案例中 模拟这些组件。当需要时可以实现按接口生成一个模拟 类作为参数传入。特别是当该类还没有完全实现时,这 种方法最为行之有效。
解决:提高可测试性 6、如果自己不负责测试工作,作为开发员在设计过程中要时刻提醒自己“我如何才能测试这些代码?我如何才能以可测试方式编写这些代码”。 7、重构是提高可测试性的主要手段
单元测试经验 测试驱动开发,开发以测试为导向 写不出测试用例,就谈不上编写单元代码 开发一个单元的代码的步骤: 1.设计和编写测试它的用例代码 2.运行自动测试,检查是否发生错误 3.编写单元的代码 4.使用前面的用例回归测试它 单元测试是编码的一部分!
单元测试经验 测试驱动开发 编写单元测试用例促进解除模块之间的耦合。先编写测试用例,强迫自己从利于调用者的角度来设计单元,关注单元的接口。为了便于调用和独立测试,必须降低单元和周边环境的耦合程度,单元的可测试性得到加强,模块化程度得到提高。这样单元的可重用性也容易被考虑和提高。
单元测试经验 重构 测试用例数量是逐步增加的,软件功能也在此过程中得到增强、更新和优化。当新的需求变化到来时,测试用例被增加或修改,难以适应测试用例的软件单元被重构。经常发生变化的测试用例和软件模块被重视和分离出来,进行重构和优化,使它们更加容易应付需求的变化。
单元测试经验 单元测试的执行要自动化。 单元测试的工作产品纳入配置管理。
欢迎提问和讨论 谢谢