软件测试修炼之道之——重现问题

运用实证(实证依赖的是观察和经验,而不是理论和纯逻辑推理)方法进行调试可以充分利用软件的独特能力来告诉你软件运行的状态,而发挥这种能力的关键是找到能够重现问题的方法。

  为什么重现问题如此重要?

  不能重现问题,就几乎不可能取得进展,因为实证过程依赖于我们观察存在缺陷的软件执行的能力。

  如何重现?

  要做的第一件事很简单,就是按照缺陷报告描述(或提示)的步骤做,要做好问题重现就要抓好控制,而需要控制的因素如下:

  1、软件本身:如果缺陷存在于最近修改的地方,那么你应该首先保证你调试运行的软件和缺陷被提交时使用的软件是同一个版本

  2、软件的运行环境:如果要与外部系统进行交互,那么可能需要确保你使用的是相同的外部系统

  3、你提供的输入:如果一段代码的运行情况受软件的配置影响很大,而缺陷又与这段代码有关,那么应该使用用户的配置来进行调试

  控制软件本身:最好的办法就是创建一个自动化的构建过程

  控制运行环境:要知道缺陷出现时软件所使用的环境,记住:软件环境包括了可能影响软件运行的所有因素。

  控制输入:要找出输入数据以便准确重现问题。如果无法获得需要的所有信息,有两种选择:一是推测一下可能的输入是什么,二是把它们记录下来。

  推测可能输入:出发点是假设问题确实存在,然后运用逆向工程的方法推测出能够导致问题的必要条件。

  回溯:通常我们知道发生了什么事,但不清楚为什么会发生,此时可以使用回溯法,如果运气好,这个逻辑可以直接重现问题,即使不能完全重现,也可以提供有用的线索,再加上其他异常的现象,就可以排除某些可能性。

  探测可能的输入值:黑盒技术中边界值分析可以在这里使用,因为输入值范围的边界是最有可能出现错误的地方,而相当于白盒测试中的边界值分析的分支覆盖,也可以在这里派上用场,可以尝试创建一些输入,使用这些输入可以覆盖同一代码块中的不同分支。

  有效地识别能重现问题的输入顺序需要你转变思路——你不必证明系统工作正常,而要证明它不正常。

  利用错误条件:当试图重现一个问题的时候,考虑一下是否有一些错误条件出现在运行的过程中,并去解释为什么问题会发生,然后,想想如何使错误条件表现出来或模拟出来,并且是否可以使你重现问题。

  引入随机性:选取一系列不同输入值的一种方法就是引入一些随机输入值。

  在推测重现问题时需要使用的数据过程中,记住:你需要验证与缺陷报告不符的结论。你找到了一种使软件出现缺陷的方法,并不意味着你找出了缺陷报告中的缺陷(尽管你已经清楚地发现了一个需要修正的缺陷)。

  记录输入值:使用日志记录输入值。获取日志最简单的方法就是在整个代码中正确地放置了对System.out.printfln()或类似方法的调用,当然,也可以使用日志框架来完成更为复杂的功能(此处略去该方法)。

  是否应该把日志留在代码中?

  答案仁者见仁智者见智,如果在代码中嵌入日志,无疑,当问题再次发生时可以快速寻找到它,但是,它却容易导致代码变得难以理解,同时,它将类似注释内容而停滞,一成不变。因此,最佳的选择是注重实效,一旦将它嵌入代码,确保你的日志随时更新,与代码保持一致,而非为了日志而做日志。

  负载和压力:关于负载测试工具的问题,通常是找到一个办法让它们重现真实的负载,但创建大量简单的交互行为可能并不会产生足够的负载来重现需要调试的问题。解决方法之一是使用日志记录真实的负载量,然后使用负载测试工具去重现它。

  压力测试工具也是类似的,只是它不直接产生负载。

  问题重现曾经是一个重要的障碍,下面我们将聊聊如何改进问题重现。不管是什么样的重现问题的方法,只要有,就比没有强。但是如何让重现问题既可靠又方便呢?

  最小化反馈周期:和软件开发的其他众多领域一样,问题重现也是要使反馈周期最小,所经过的周期越短,反馈就越及时,其相关性就越高。

  因此,最先要关注的就是找出问题重现中哪些方面是不需要的,将它们剔除掉,称为将问题重现最小化。那么,哪些元素可以被剔除呢?往往这要靠直觉。你了解软件,并且知道哪些模块可能被一些特定输入所影响,如果直觉不对,那么一些非直接的方法可能会帮助到你。

  改进问题重现不是一蹴而就的事,而是在整个诊断过程中要牢记在心的东西。

  将不确定的缺陷变为确定的:要做到这一点,需要明白不确定性从何来?

  1、开始于不可预知的初始状态

  当你的软件从未经初始化的内存读取数据时,通常会出问题。如果你有理由相信,是这个原因导致了不确定性问题,那么你最好的选择可能是使用调试内存分配器,来强制内存被初始化为一个众所周知的值,或用内存完整性检验软件来检测是否引用了未初始化的内存。

  2、与外部系统进行交互

  由此引起的不确定性问题往往不是因为二者表现不一,而是因为时间上微妙的不同。解决的策略是能够精确控制从外部系统接收了什么,以及何时接收的。最好的选择可能不是试图直接控制外部系统,而是用你能控制的东西替换它,比如调试子系统或代替测试。

  3、故意地使用随机性

  由此导致的不确定性听起来还是很正常的,幸运的是,大部分所谓使用随机数的软件都是通过确定性算法产生的伪随机数,因此这个是完全可以预测的行为。

  4、多线程

  由此引起的不确定性最难处理。在多核系统盛行的今天,往往我们处理的并不是真正的并发。而在缺乏并发控制的结构化方法下,我们不得不依靠一些特殊的方法。因此,在处理方法中最有效的工具之一就是不起眼的sleep()方法,它允许你强制一个线程长时间等待而出现竞争状态。

  例如,假设你正在工作的软件中多个乖哦工作线程并行处理工作项目,工作线程使用下面的java代码来获得工作项目:

if (item=workQueue.lockWorkItem()) 

item.process(); 
workQueue.writeResultAndUnlock(item); 
}

  你试图跟踪一个间歇出现的缺陷,有时同一个工作项目会同时分配给两个工作线程。遗憾的事,这种情况极少出现,那么,你可以将代码更改为如下形式来增加重现该问题的几率:

if (item=workQueue.lockWorkItem()) 

Thread.sleep(1000); 
item.process(); 
workQueue.writeResultAndUnlock(item); 
}

  注意:尽管sleep()方法在重现问题和诊断阶段很有用,但在修复缺陷阶段它不是一个适合的方法。

 自动化:一个自定义测试不仅能够方便的运行,而且当诊断结束开始修复的时候,对于即将编写完成的测试来说,它是一个很好的起点。如果确定缺陷重现需要通过日志,那么可以选择重放日志文件。

  迭代:在诊断的过程中,你构建了足够多关于如何以及为何软件如此运行的信息,可以用此不断改进重现,如下图:

  通过如下步骤反复改进:

  1、你确定一个特定的模块已包含在内,其中有导致缺陷的元素,这样可以创建一个更小的文件。

  2、进一步诊断,发现能通过用桩模块替代与第三方服务器交互的子系统,让问题每次都出现,桩模块可以很容易返回已经确定的响应。

  3、最后,把跟踪范围缩小到一个特定函数,通过设置一组具体的参数调用该函数来创建单元测试,以便重现问题。

  如果真的不能重现问题该怎么办?

  首先,不要轻易给出“缺陷不存在”的结论,除非尽力获取了更多额外信息,用尽了所有可用的办法依然不能重现问题。其次,在相同区域解决不同的问题,尽管没有你目前跟踪的缺陷那么严重和紧急,也许它们蒙蔽了真正缺陷所在,也许会让你找到重现问题的关键因素,当然,也许没有任何帮助。第三,试着让其他人参与其中,这样可以获取其他人的不同角度看待问题,尤其是反馈错误的人。第四,充分利用用户群体,有些时候,缺陷出现在外部系统而非开发系统中,但这需要用户为你收集所需要的信息,从某种角度来说,并不理想。最后,可以使用推测法,你所需要做的是把自己融入到软件中,在想象中执行软件,执行每一步,考虑有哪些出现错误的可能性,尝试解释你跟踪的缺陷。

  正常来说,我们有能力重现问题,而在下一章中,我们将会看到如何用重现来诊断问题。

最新内容请见作者的GitHub页:http://qaseven.github.io/

时间: 2025-01-24 17:46:48

软件测试修炼之道之——重现问题的相关文章

软件测试修炼之道之——诊断

诊断问题是程序调试的关键,这个阶段,我们可以开始解决缺陷问题了,你可以了解看到的运行结果背后的根本原因. 真正有效的缺陷修复要求思维方式既开放又有条不紊,解决方法既创新又注重全面综合,这和软件开发的其他很多方面是一样的. 一种调试方法:提出一个可能提供解释的假设,然后再构建实验去证明你的假设,如下: 1.按照你对软件运行情况的理解,提出一个可以导致这种运行状况的假设 2.设计一个实验,证明你的假设正确与否 3.如果你设计的实验不能证明你的假设,那么重新设计一个实验,然后再次进行实验 4.如果实验

《Ruby程序员修炼之道》(第2版)目录—导读

版权 Ruby程序员修炼之道(第2版) • 著 [美] David A. Black 译 钟凤鸣 陈雪静 责任编辑 杨海玲 • 人民邮电出版社出版发行 北京市丰台区成寿寺路11号 邮编 100164 电子邮件 315@ptpress.com.cn 网址 http://www.ptpress.com.cn • 读者服务热线:(010)81055410 反盗版热线:(010)81055315 版权声明 Original English language edition, entitled The W

项目经理修炼之道(2) -- 必须读的书 .

软件这个行当里历来有个谣言:项目经理不懂技术没关系. 有人说这事儿是外国的先进经验,但我怀疑这是杜撰的. 这一观点的潜台词是:项目经理是管理者,指挥下属就行了,干嘛要懂技术! 这就像说班长不用拿枪上战场一样可笑. 持这个观点的可还记得:"将军起于行伍,宰相拔于州郡"这一说. 我的观点是,项目经理一定要懂技术,并且还要有比较扎实的功底,虽然在专门领域上不一定是专家. 在这篇文章里,我们将列几本用来打根基的书,这些书要精读而不能翻翻就算了. 这些书的用途,不在眼前,但却最终决定你的成长高度

《Ruby程序员修炼之道》(第2版)—第1章1.1节进入Ruby的世界

第1章 进入Ruby的世界 Ruby程序员修炼之道(第2版) 本章主要内容 Ruby语法的生存工具箱① Ruby基础编程指引:程序编写.保存.运行和错误检查 Ruby安装指南 Ruby的扩展机制 Ruby中易用的命令行工具,包括交互式Ruby解释器(irb) 本书的内容是Ruby基础,而本章是基础中的基石.本章的目标是让读者在开始学习Ruby之前掌握足够的知识和技巧. 接下来读者将看到Ruby的基本语法和技术,以及Ruby的运行机制:如何写一个程序,怎样使用Ruby运行程序,以及如何把一个程序分

PostgreSQL修炼之道:从小工到专家

数据库技术丛书 PostgreSQL修炼之道:从小工到专家 唐成著             图书在版编目(CIP)数据 PostgreSQL修炼之道:从小工到专家/唐成著. -北京:机械工业出版社,2015.4 (数据库技术丛书) ISBN 978-7-111-49872-8 I. P- II. 唐- III. 关系数据库系统 IV. TP311.132.3 中国版本图书馆CIP数据核字(2015)第063966号 PostgreSQL修炼之道:从小工到专家 出版发行:机械工业出版社(北京市西城

Spark修炼之道系列教程预告

课程内容 Spark修炼之道(基础篇)--Linux基础(15讲).Akka分布式编程(8讲) Spark修炼之道(进阶篇)--Spark入门到精通(30讲) Spark修炼之道(实战篇)--Spark应用开发实战篇(20讲) Spark修炼之道(高级篇)--Spark源码解析(50讲) 部分内容会在实际编写时动态调整,或补充.或删除. Spark修炼之道(基础篇)--Linux大数据开发基础(15讲). Linux大数据开发基础--第一节:Ubuntu Linux安装与介绍 Linux大数据开

Spark修炼之道——Spark学习路线、课程大纲

课程内容 Spark修炼之道(基础篇)--Linux基础(15讲).Akka分布式编程(8讲) Spark修炼之道(进阶篇)--Spark入门到精通(30讲) Spark修炼之道(实战篇)--Spark应用开发实战篇(20讲) Spark修炼之道(高级篇)--Spark源码解析(50讲) 部分内容会在实际编写时动态调整,或补充.或删除. Spark修炼之道(基础篇)--Linux大数据开发基础(15讲). Linux大数据开发基础--第一节:Ubuntu Linux安装与介绍 Linux大数据开

《Linux内核修炼之道》——分析内核源码如何入手?(上)

<Linux内核修炼之道>--分析内核源码如何入手?(上) 透过现象看本质,兽兽门无非就是一些人体艺术展示.同样往本质里看过去,学习内核,就是学习内核的源代码,任何内核有关的书籍都是基于内核,而又不高于内核的. 既然要学习内核源码,就要经常对内核代码进行分析,而内核代码千千万,还前仆后继的不断往里加,这就让大部分人都有种雾里看花花不见的无助感.不过不要怕,孔老夫子早就留给我们了应对之策:敏于事而慎于言,就有道而正焉,可谓好学也已.这就是说,做事要踏实才是好学生好同志,要遵循严谨的态度,去理解每

《Ruby程序员修炼之道》(第2版)—第1章1.4节易用的Ruby工具和应用程序

1.4 易用的Ruby工具和应用程序 安装Ruby后,就可以得到一组重要的命令行工具,它们被安装在配置信息bindir所指定的文件夹中,通常是/usr/local/bin./usr/bin或者/opt同等的目录中.(可以使用require "rbconfig"去测试一下RbConfig::CONFIG["bindir"]返回的结果.)这些命令行工具具体是以下几个. ruby:解释器. irb:Ruby交互式解释器. - rdoc和ri:Ruby文档工具. rake: