标题 | 面向小规模程序的轻型测试方法 |
范文 | 叶常春++尹良泽 摘要:初级程序员开发的程序一般是小规模程序。对于小规模程序,软件工程实践采用的重型测试方法是不适用的,因为测试代码量远大于程序代码量。论文描述三种面向小规模程序的轻型测试方法。第一种方法是“使用freopen函数的方法”,做法是在程序中调用freopen函数把输入输出重定向至文件,并用文件比较命令来比对输出结果和期望结果是否一致。好处是免除手工输入和人工比对,消除繁琐和减少犯错的机会。第二种方法是“使用重定向和批处理程序的方法”,实质与第一种做法类似,但能够批处理地使用多组测试数据对程序进行测试。第三种方法是“轻型单元测试方法”,做法是把程序分解成多个函数,对函数进行测试。好处是便于定位错误。上述三种轻型程序测试方法的优点是易学易用,适用于在OJ网站刷题和计算机软件能力认证考试等场合。 关键词:软件工程;轻型测试方法;小规模程序;重定向;单元测试 中图分类号:TP3 11.1 文献标识码:A DOI:10.3969/j.issn.1003-6970.2015.10.024 引言 这些年,越来越多的大学生和高中生在OJ(Online Judge,在线判题网站)上做编程题,俗称“刷题”。初级程序员经常遇到这样的情况:程序在自己的电脑上运行明明是对的,但在网站上提交代码后,网站却报告答案错误。原因是什么呢?这是因为自己运行时,输入的数据只考虑一种或几种情形,在这些情形下程序运行结果是正确的。但在另外的情形下,程序运行结果将是不正确的。拿排序程序来讲,如果输入已经排好序的数据,程序能正常工作,那么就能说程序百分百正确了吗? 初级程序员还经常遇到另一种情况,就是程序运行一直异常终止或者结果总是不对,不得不一遍又一遍地修改,一遍又一遍地运行程序。遇到这种情况是很耗费时间的。如果加上以下两种状况,时间耗费就更严重了。第一种状况是每次运行都在命令行终端上输入一大批数据。第二种状况是程序输出内容多又长。拿多又长的输出内容和期望输出进行人工比对,不仅耗时而且容易犯错。 假如在计算机协会举办的软件能力认证考试中遇到以上问题,考生的表现将大打折扣。除此之外,另一个阻碍考生在软件能力认证考试中拿高分的因素是:针对所提交的程序代码,考试服务器不提供任何反馈。这与OJ的做法不同。针对所提交的程序代码,OJ会提供诸如编译错误、超时、答案有误和答案正确等反馈。 本文阐述解决以上问题的有效方法。方法的本质是对程序进行测试。这一本质与软件工程实践采用的软件测试方法是相同的。但不同的是,本文阐述的程序测试方法是轻型方法,而软件工程实践采用的方法是重型方法。初级程序员编写的程序一般是小规模程序(代码行数多在300行以下)。对于小规模的程序,采用重型测试方法是不现实的。原因在于,测试代码量(以基于xUnit的单元测试为例),将远远大于程序代码量。对于小规模的程序,本文阐述的轻型测试方法是非常适用的。近两年的程序设计教学过程中,学生的实践已经证明了这一点。这一方法也可应用到测试驱动开发中。 本文阐述三种轻型测试方法。第一种是使用freopen函数的测试方法,在第二节展开叙述。第二种是使用重定向和批处理程序的方法,在第三节展开叙述。第三种是轻型单元测试方法,在第四节展开叙述。第五节是结束语。 1 使用freopen函数的方法 这一做法是在程序中调用freopen函数。该函数的功能是把文件重定向为标准输入,或把标准输出重定向至文件。freopen函数声明如下: FILE*freopen(const char*filename,const char*mode,FILE*stream): 其中,参数filename是文件路径。参数mode是文件打开方式,例如“r”代表读打开,“w”代表写打开。参数stream是已经打开的文件流。函数的功能是:(1)关闭stream关联的文件流。(2)以mode方式打开filename所指定的文件file;(3)把文件file与文件流stream相关联。(4)如果函数执行失败,则返回NULL。图1举例说明freopen函数的用法。 图l中,stdin代表标准输入,stdout代表标准输出。“freopen(“in.txt”,“r”,stdin)”是把标准输入重定向到in.txt文件。这样,当程序从标准输入读取数据时,不再从键盘读取,而是从in.txt文件读取。“freopen(”out.txt”,”w”,stdout)”是把标准输出重定向到out.txt文件。这样,当程序向标准输出写数据时,不再写到屏幕上,而是写到out.txt文件中。值得提醒的是,freopen函数的第一个参数可以是任意的文件路径。提交代码的时候,要把调用freopen函数的相关语句注释掉。 利用freopen函数测试程序的做法是:(1)编辑测试用的输入数据,保存至文本文件IN中。编辑包含正确输出结果的文本文件EXPECTED。(2)在程序中,把标准输入重定向至文本文件IN,把标准输出重定向至文本文件OUT。(3)比对OUT文件与EXPECTED文件,如果内容一致,则测试通过,否则测试不通过。 使用freopen函数的好处有两点。一是减免了手工输入数据的环节。这在反复运行程序的情形下是有帮助的。二是可以利用文件比较命令来比对运行结果文件和期望结果文件是否一致。Windows系统中,文件比对命令fc能够比对两个文件的内容是否一致。fc命令的用法如下: fc /Nd:\tmp\out.txt d:\result\expected.txt 上述命令比对的是out.txt文件和expected.txt的内容是否一致。如果一致,将报告两个文件无差异;否则将分别显示两个文件相异之处的行号及内容。Linux系统中,文件比对命令是diff命令,用法类似于fc命令。 在程序中调用freopen函数这一做法的毛病在于一次运行只能处理一组测试数据。要处理另一组测试数据,要么改动调用freopen函数所使用的文件路径参数,要么更换输入文件的内容。下面阐述的“使用重定向和批处理程序”的做法能一次处理多组测试数据。 2 使用重定向和批处理程序的方法 假设编译一个源程序生成的可执行程序叫shuZuQuShu.exe,那么我们可以在命令行界面运行该程序并且进行输入输出重定向,见图2。 上图中的命令行中,“c:\Users\yeah>”是命令行提示符。shuZuQuShu是命令名(省略了后缀名“.exe”),即程序名。“ 要用另一组数据来检验该程序,命令可以换成: shuZuQuShu 效果是拿in2.txt文件代替键盘输入,把输出写入到out2.txt文件中。查看out2.txt文件的内容,你可以判断程序是否正确运行。 一旦需要反复运行程序,在命令行界面中输入包含重定向的命令有些繁琐。一个更好的做法是使用批处理程序。图3是一个批处理程序的示例。批处理程序中,每一行都是一条命令。例如,图3的第一行是运行shuZuQuShu程序。第二行是运行文件比对命令fc,其中no_sell_expected.txt是期望的结果文件。该批处理程序的功能是多次运行shuZuQuShu.exe这个程序,每次运行把不同的数据文件重定向为标准输入,把标准输出重定向至不同的结果文件,然后比对输出结果文件和期望结果文件。运行这样的批处理程序一次,能用多组测试数据来验证程序的正确性。 批处理程序是一个文本文件,可用一个编辑器(比如notepad)编辑生成。Windows系统中,批处理程序文件的后缀名是“.bat”,取的是batch(中文翻译是批处理)的前三个字母。Linux系统中,则可以使用脚本程序来完成类似功能。 运行一个批处理程序的方法是:启动命令行界面,用cd命令(切换文件夹命令,有时还要用到切换盘符命令)把当前文件夹切换到批处理程序所在文件夹。然后敲入运行批处理程序的命令(见以下示例中下划线部分),例如: C:\Users\yeah>runTestcases.bat 要注意的是,一个批处理程序要能够找到它用到的各类文件,例如命令程序文件和数据文件。上述批处理程序要在自己所在的文件夹的debug子文件夹内能找到shuZuQuShu.exe文件,也能够找到testdataYno_sell_in.txt文件。 “利用重定向和批处理”的方法是对程序整体进行测试。这种方法的不足是:在程序有错误时,能告诉你不正确,但没有提供错在哪里的线索。下面描述的轻型单元测试方法能帮助你定位错误的位置。 3 轻型单元测试方法 轻型单元测试方法包含两个环节:编写完成程序功能的函数(叫做功能代码)和编写测试函数正确性的代码(叫做测试代码)。这里强调,写功能代码时避免使用大函数,要把大函数分解成若干短小的函数,分别测试。这样就容易定位错误和改正错误。 下面举例讲解轻型单元测试方法。示例使用的程序题目取自2015年3月软件能力认证考试的第3题,题目名称为“节日”。该题目提供的输入有两个年份yl和y2,月份m,第n个星期d(d=l,2,…,7,7对应星期日);要求输出对于yl年到y2年之间的每一年,该年m月第n个星期d是几月几号。该题的代码见代码清单1。限于篇幅,仅仅给出部分代码(用省略号代替部分代码),但足以说清楚轻型单元测试方法是什么和怎么做。完整的程序代码一共有6个功能函数(不含主函数)。我们测试了其中两个担心会出错的函数。 说明几点: l.testcases函数的作用是调用其他测试函数。这样做能避免在源文件上部声明每一个测试函数。 2.assert本质上是一个宏,叫做assert断言,用法类似于函数。它的作用是验证括号内的表达式是否为真,如果不为真则中断程序执行并报告程序错误。只有测试函数内所有assert断言都为真,测试才能够通过。 3.如果上例中的get_date_in_month函数不是返回日期值,而是输出该值到屏幕,则无法对其进行测试。 当一个测试函数中的assert断言不成立时,表明与之相对的被测函数存在错误。这就帮助我们定位了错误。函数越简短,定位错误越精准,改正错误也就越容易。要特别注意,测试数据需细心准备。用错误的测试数据(无论是输入数据还是期望的输出数据)来测试函数,即使被测函数逻辑正确,也会被视为错误。 轻型单元测试方法简便实用。但它无法对程序输入环节进行测试。 4.结论 本文描述的轻型程序测试方法的优点是易学易用,适用于在OJ网站刷题和计算机软件能力认证考试等场合。在这些场合,程序员编写的程序一般属于小规模程序(代码量多在300行以下)。软件工程实践采用的重型测试方法不适用。原因在于测试代码量显著大于程序代码量。 上面描述的三种轻型程序测试方法各有所长。“使用freopen函数的方法”一次只能处理一组测试数据,而“使用重定向和批处理程序的方法”一次能处理多组测试数据。这两种方法都利用了文件流重定向,对整个程序进行测试。它们的不足是在程序有错误时,能告诉你不正确,但没有提供错在哪里的线索。轻型单元测试方法能够定位错误,但无法测试程序输入输出环节。只要轻型单元测试方法与其他两种方法的任一种结合使用,就既能定位错误,又能测试程序输入输出环节。 |
随便看 |
|
科学优质学术资源、百科知识分享平台,免费提供知识科普、生活经验分享、中外学术论文、各类范文、学术文献、教学资料、学术期刊、会议、报纸、杂志、工具书等各类资源检索、在线阅读和软件app下载服务。