运用实证(实证依赖的是观察和经验,而不是理论和纯逻辑推理)方法进行调试可以充分利用软件的独特能力来告诉你软件运行的状态,而发挥这种能力的关键是找到能够重现问题的方法。
为什么重现问题如此重要?
不能重现问题,就几乎不可能取得进展,因为实证过程依赖于我们观察存在缺陷的软件执行的能力。
如何重现?
要做的第一件事很简单,就是按照缺陷报告描述(或提示)的步骤做,要做好问题重现就要抓好控制,而需要控制的因素如下:
1. 软件本身 如果缺陷存在于最近修改的地方,那么你应该首先保证你调试运行的软件和缺陷被提交时使用的软件是同一个版本 2. 软件的运行环境 如果要与外部系统进行交互,那么可能需要确保你使用的是相同的外部系统 3. 你提供的输入 如果一段代码的运行情况受软件的配置影响很大,而缺陷又与这段代码有关,那么应该使用用户的配置来进行调试
控制软件本身:最好的办法就是创建一个自动化的构建过程
控制运行环境:要知道缺陷出现时软件所使用的环境,记住:软件环境包括了可能影响软件运行的所有因素。
控制输入:要找出输入数据以便准确重现问题。如果无法获得需要的所有信息,有两种选择:一是推测一下可能的输入是什么,二是把它们记录下来。
推测可能输入:出发点是假设问题确实存在,然后运用逆向工程的方法推测出能够导致问题的必要条件。
回溯:通常我们知道发生了什么事,但不清楚为什么会发生,此时可以使用回溯法,如果运气好,这个逻辑可以直接重现问题,即使不能完全重现,也可以提供有用的线索,再加上其他异常的现象,就可以排除某些可能性。
探测可能的输入值:黑盒技术中边界值分析可以在这里使用,因为输入值范围的边界是最有可能出现错误的地方,而相当于白盒测试中的边界值分析的分支覆盖,也可以在这里派上用场,可以尝试创建一些输入,使用这些输入可以覆盖同一代码块中的不同分支。
有效地识别能重现问题的输入顺序需要你转变思路——你不必证明系统工作正常,而要证明它不正常。
利用错误条件:当试图重现一个问题的时候,考虑一下是否有一些错误条件出现在运行的过程中,并去解释为什么问题会发生,然后,想想如何使错误条件表现出来或模拟出来,并且是否可以使你重现问题。
引入随机性:选取一系列不同输入值的一种方法就是引入一些随机输入值。
在推测重现问题时需要使用的数据过程中,记住:你需要验证与缺陷报告不符的结论。你找到了一种使软件出现缺陷的方法,并不意味着你找出了缺陷报告中的缺陷(尽管你已经清楚地发现了一个需要修正的缺陷)。
记录输入值:使用日志记录输入值。获取日志最简单的方法就是在整个代码中正确地放置了对System.out.printfln()或类似方法的调用,当然,也可以使用日志框架来完成更为复杂的功能(此处略去该方法)。
是否应该把日志留在代码中?
答案仁者见仁智者见智,如果在代码中嵌入日志,无疑,当问题再次发生时可以快速寻找到它,但是,它却容易导致代码变得难以理解,同时,它将类似注释内容而停滞,一成不变。因此,最佳的选择是注重实效,一旦将它嵌入代码,确保你的日志随时更新,与代码保持一致,而非为了日志而做日志。
负载和压力:关于负载测试工具的问题,通常是找到一个办法让它们重现真实的负载,但创建大量简单的交互行为可能并不会产生足够的负载来重现需要调试的问题。解决方法之一是使用日志记录真实的负载量,然后使用负载测试工具去重现它。
压力测试工具也是类似的,只是它不直接产生负载。
问题重现曾经是一个重要的障碍,下一节我们将聊聊如何改进问题重现。