当写完程序的所有代码后您一定会想: “ 终于大功告成了,哈…… 。
慢着!如果您真的以为万事大吉就错了,还有一件非常重要的事情等着我们去做,那就是对程序的测试和调试。
据说很多年前当一个小虫子在一台计算机中使一些晶体管不能工作时,才第一次用到了调试 (DEBUG) 一词。所以,术语 DEBUG 也就是找出程序中的 “ 小虫子 (BUG) 。不管它的由来到底是什么,它的目的就是查找使程序失败或产生不正确结果的原因。
在开发应用程序中应将测试作为一个独立的并且是有计划的任务。测试软件的方法有很多种。而有些开发者只在完全编写完应用程序后才进行测试。 VFP 所具有的交互功能使与开发过程中并存的测试变得更容易也更有效果。问题是管理跟踪与开发过程并存在测试所花费的时间比较困难。这一课将考察各种测试技巧并分析它们的优缺点。
数据驱动和逻辑驱动的测试
测试应用程序的两个要素是:有效性和范围。有效性测试是检查应用程序是否对特定的输入产生预期的结果。范围是检查所有的语句是否都已被测试执行,任何没有被执行的代码都有可能隐藏故障。
有效性测试有两种基本的方法。第一种方法是数据驱动的,它不需要知道关于程序的工作方式方面的知识,而主要集中在对现实世界或虚构的数据进行采样的基础上 , 挑选出一系列测试数据集合,然后使用这些数据运行程序,看它是否产生了预期的结果。
另一种方法是逻辑驱动的方法,这种方法需要程序编码的广泛知识,它试图测试程序可以执行的每个路径,还通过使用接近和超过已知的实际上存在限制的数据,对程序如何处理这些数据限制进行检测。
这两种方法各有其优点和不足:
数据驱动应用程序的优点包括它有意识或无意识地对程序进行假设。但程序员经常 假设 一个程序绝不会执行某种动作,从而也就不能彻底地测试它。而有时程序员假定是正确的部分往往正是出现问题的部分。数据驱动方式的主要不足是不能保证测试数据集合覆盖了所有的程序路径和循环。
逻辑驱动的方式测试弥补了数据驱动测试的弱点。如果设计得好,它可以测试整个系统中的每一行代码。它明显的缺点是对于大型应用程序来说,全部测试每一行代码需要多重数据测试。更进一步说,它需要花费很多时间开发出必要的数据集合以保证对每一行代码的测试。
测试技术
一旦开始进行编码,讨论的重点就从纸上思维方式的检查转移到正规的编码和实际物理测试上,下面描述了一些技术:
正式的代码审查是对重要代码的仔细的检查,它提供了对代码标准、注释的使用、变量的命名、局部和全局变量的使用等的反馈。
模型化或原型化是使用可以获得的工具快速地创建应用程序的外壳。它的目的是显示系统的整体功能。在 VFP 中,它牵涉到创建与一个简单菜单相连系的基本表单和报表。虽然表单是有功能的,但它们也许没有包括最终的事件捕获。与之类似,报表也许没有包括选择和排序的选项。目前这种技术被称为快速应用程序开发。
语法检查测试代码的基本正确性。它检查命令和文本串的拼写、表达式的有效性以及命令的基本结构。 Foxpro 在编译中进行的大部分是这种工作,当然有些语法问题只有到运行程序时才会表现出来。
单元测试用来检查一些独立的语句组。例如,在设计一个新的类定义时,使用单元测试检查与它的方法有联系的代码。为了实现单元测试,必须编写一种称为驱动程序的特殊程序。驱动程序不成为最终代码的组成部分,而是用来设置测试代码段所必须的程序环境,如初始化变量。
虽然可以分别测试各个过程和函数。但是,它们的正确运行并不能完全保证程序作为一个整体也可以正确的工作。单元测试可以减少整个程序系统错误产生的可能性。测试主要是对模块之间接口的连接进行检查,它的目的是何证数据和逻辑正确地从一个模块传递到另一个模块,其中包括了使用正确的参数类型和大小。它还可以寻找由被调用过程或函数重定义而对公共或私有变量进行的意外的修改。
功能测试检查程序工作的主要特性是否象预期的一样。当从菜单中选定了一个报表选项时,是否能得到所选定的那个报表或者是另一个报表,甚至是一个表单呢 ? 如果一个表单中显示出的消息说,按下 F2 来显示可能值的列表,它就会检查在你按下 F2 时是否真的出现了这个列表,它不需要对报表结果或按 F2 时显示的那个列表是否是包括了正确的数据进行验证。
强度测试检查程序的边界条件。它对程序如何响应极限值进行测试。例如,如果程序员负责记录每周的工资单,它也许会检查,如果输入 170 作为雇员的一周工时数,程序会做些什么 ( 一周的工时最大值 7 乘以 24 ,即 168) ,对网络的强度测试主要是当多个用户运行程序时,程序如何动作,程序是否能处理数据的并发更新 ? 记录和文件锁是否工作正常,而同时又能最小化其他用户不能访问数据的时间 ?
程序的运行速度归于性能测试一类。 VFP 提供了用多种方式编写出大多数特性的灵活性的方法,但这些方法的性能并不完全相同,有些方法比其他的性能好得多。性能测度通过识别程序的那些部分花费了最多时间来寻找能提高速度的地方。然后就可以试着用其他的编程方法来提高速度。
创建测试环境
创建一个测试环境要考虑两个重要问题:硬件和人的问题。在测应用程序时,测试它的硬件当与计划使用它的配置相同。如果要在网络上运行应用程序,则在一个单独的系统上测试它会漏掉与记录和文件共享有关的潜在问题。
在测试快结束时使用真实的数据是很重要的。因此,推荐的开发模式是首先创建和实际收集数据所必须的哪些模块。然后用户可以使用它们输入真实的数据,这些数据又可作为以后开发的测试数据。
当然,所有的开发工作都应与真实数据是物理隔离的。在开发过程中很有可能会发生破坏真实数据的错误。例如:错误的输入真实数据的路径而不是测试数据的路径会破坏真实的数据。这也意味着应该定期备份测试数据测试程序。
不是每个人都擅长测试的。实际上,有些人比其他人更乐于进行测试。应用程序的开发者通常是最差劲的测试者,他们太了解程序而且倾向于总是输入正确的数据。他们下意识不想使自已的程序失败,虽然他们也很清楚地认识到进行测试的必要性。
另一方面,其他人员不会和程序有感情,也就能在测试中更加不留情面。虽然 “ 无情的 测试者会发现更多的问题,但他们必须与开发者进行讨论。程序毕意是别人创建的产品,这点无法否认。
VFP 提供了很多不同的方法来完成同一个任务,所以不要因为与你的方法不同而批评别人的方法。如果存在另一种方法能更好、更有效地完成任务,可以向其他程序员演示它为什么更好。帮助别人学习,而不是强求他们学习新的方法。
确定测试何时结束
有人也许会说测试永远也不会结束,直到应用程序变得过时,废弃不用为止。实际应用中总会出现数据的新组合,大多数程序会不断发生变化,在这里加个新特性或在那里加个字段。每个新特性都会引入产生错误的可能性。程序路径也可能发生变化,新的变量可能覆盖其他例程中的同名变量,而且其他的副作用也会影响现在的代码。
不管使用哪种方法都几乎不可能确定什么时候找出所有的故障。更多的时候是根据找出一个故障与放任它存在的代价做出的猜测决定。相反,仅仅为了如期完成测试计划就宣布测试结束是危险的,缺少远见的。测试应当一直持续到每个参加工作的人都对应用程序的性能感到有信心才结束。每个程序员进行的代码测试的方法都略有不同,因此,即使仅有两名程序员在一起进行测试,都会显著提高整体的检测率。
跟踪错误的方法
尽管前面讨论了避常见错误和测试的方法的开发,但是错误仍然会发生。最首要的任务是在发生错误时找到它并修改它。
在程序执行过程中发生错误时, VFP 显示出一个出错提示框,并给出简单的出错信息。在信息下方有三个按钮; < 终止 >< 挂起 >< 忽略 > 。大多数情况下不想忽略错误。如果是为其他用户编写程序,你决不会希望忽略错误。但事情总有例外。如果程序因为找不到颜色集而失败,就可能希望忽略这个错误。大部分错误是不能忽略的。如果 VFP 不能定位一个表格,则忽略错误没有任何意义。因为某些时候可能会用到这个表格,所以可将问题存档并退出程序。
作为开发者,读者会发现《挂起》是测试中的一个选项。它停止程序的执行而不是从内存中删除当前的变量。任何当前已打开的表格仍然保持打开,它们的记录指针也在各自的位置上。这时我们可以打开调试窗口( debug )和跟踪窗口( trace )来查看正在被执行的源代码及各种变量和表格的状态。因为已经在前面的章节里讲过了调试工具的使用方法,在这里就不重复了。
实际上,可能不希望让客户看到这种 VFP 的出错信息。而是你自已的错误处理程序来捕获所有的错误,错误处理程序将发生错误时的系统存档,并安全的退出应用程序。
下面就讲一下如何编写自已的错误处理程序。
ON ERROR 正是我们想要的命令,它可把程序定向到一个错误处理程序中,它的主要任务是提供关于错误的信息。以下这条命令的执行将使后续程序的错误定向到 MYERROR 错误处理程序中:
ON ERROR DO MYERROR WITH error(),message(),message(1),sys(16),lineno()
ON ERROR 的语法如下:
ON ERROR [ 命令 ]
其中, [ 命令 ] 项指出当程序出现错误时将要执行的命令,它即可以是一条简单的命令也可以调用其他的过程。如果省略它的话将使前次定义的 ON ERROR 失去作用。
在上面的例子中我们定义的是当程序错误时执行
DO MYERROR WITH error(),message(),message(1),sys(16),lineno()
命令来调用名为 MYERROR 的错误处理过程。其中 error(),message(),message(1),sys(16),lineno() 为传递给过程的参数,它们分别传递“错误号,“错误信息,“返回导致这个错误的程序源代码,“当前执程行的程序名,“正在执行的那一行程序的行号给错误处理过程。在 MYERROR 过程中可以通过检查错误号来确定错误的类型。
从用户的角度来说主要存在三种主要类型的错误。第一种是轻微错误,其中包括的错误有找不到颜色集,或者打印机或软盘驱动器没有就绪。有些情况下只要登记错误,然后跳过引起错误的命令并继续执行,如找不到颜色集。
其他情况下,在发给用户的消息后是一个处理错误的 RETRY 或 RETURN 命令。使用这种方法的例子是程序试图从软盘驱动器上读文件时,驱动器中没有软盘或者程序试图写盘时软盘是写保护的。在这种情况下,将显示给用户消息,告诉他们如何用 RETRY 纠正错误。
另一个例子假设使用 SKIP 在记录中移动时超过了文件的尾。它的错误号为 4. 在遇到这种类型的错误时不必取消程序。为了纠正问题,程序只要把记录指针重置到表格的最后一条记录上即可,程序清单如下:
prodedure myerror
lparameters lnErrorNo,lcMessage,lcErrorLine,lcmodule,lnErrorLineNo
if lnErrorNo = 4
messagebox(“ 到了记录尾了 )
gotobottom
else
cancel
endif
return
另外我们还可以用一个文本文件来记录程序的出错信息,以便程序员查阅。可以用记录本建一个名为 ERROR.TXT 的文件, COPY 到程序所在的目录下。然后在错误处理程序中把各种错误信息写到文本文件里。下面是一个简单的小例子。
prodedure myerror
lparameters lnErrorNo,lcMessage,lcErrorLine,lcmodule,InErrorLineNo
local myerror
c=ttoc(datetime())+" 错误号 :"+str(lnErrorNo)+" 提示信息 :"+lcMessage+;
" 代码 :"+lcErrorLine+" 文件名 :"+lcmodule+" 行号: "+str(InErrorLineNo)
a =fopen("error",12)
b = fread(a,1024)
do while !feof(a)
b=fread(a,1024)
enddo
fput(a,c)
fclose(a)
d=messagebox(c,2+16+0," 错误 ")
do case
case d= 3
cancel
case d= 4
retry
case d= 5
return
endcase
到此为止,伴随着第二十章的结束整个中级教程也结束了,希望您学到更多的知识,我们高级教程再见。