基于doctest的Python程序自动评测方法研究

2021-04-29 06:56亓雪冬葛元康
微型电脑应用 2021年4期
关键词:子类测试用例评测

亓雪冬, 葛元康

(中国石油大学(华东) 1.信息化建设处; 2.计算机科学与技术学院, 山东 青岛 266580)

0 引言

近年来,Python语言以其简洁的语法、灵活的交互性与动态性和丰富的第三方程序库等特点快速被国内外高校所认可。嵩天等提出,Python语言是程序设计课程教学改革的理想选择[1]。众多高校中开设了大量以Python语言为载体的程序设计类课程,以我校为例,2019—2020学年约12个专业(20多个班级)在全校公共基础课《程序设计》课程中,将授课编程语言改变为Python。新的选择给教学工作带来机遇的同时也带来了挑战。

程序自动评测系统能够快速对学生编写的程序进行自动评判[2-3],实现了批改和反馈的自动化[4-5],是程序设计类课程教学实践环节不可或缺的教学工具,是提高学习效率、保障教学效果和提升教学质量的重要技术手段。因此,研究Python程序自动评判方法对于教学改革具有重要的现实意义。本文在充分研究Python标准库原有测试模块doctest的基础上,根据教学实践环节的实际需求,提出了扩展和改造doctest编程模型的思路和方法,实现了Python程序的全自动评判。

1 doctest自动化测试工具

doctest是Python标准库中的自动化测试模块,根据编写在文档字符串中的测试用例对待测试模块的功能进行测试。文档字符串(DocStrings)是Python模块、类或函数定义中的起始语句,可看作多行注释,定界符为三个单引号(' ' ')或者三个双引号(""")。将测试用例放入文档字符串中,易于查看和修改。测试用例格式与Python交互式环境中执行语句的格式完全一致。

1.1 doctest对象模型

doctest的核心测试功能由2个容器类和4个处理类提供。对象模型[6],如图1所示。

图1 doctest对象模型

两个容器类分别为Example和DocTest,用于存储测试文本中提取的测试用例。其中,Example表示单个测试用列,由一条Python语句和其对应的输出组合而成;DocTest表示Example(测试用例)的集合。

四个处理类分别为DocTestFinder、DocTestParser、DocTestRunner和OutputChecker。其中,DocTestFinder用于发现测试模块中的文档字符串;DocTestParser用于分析并返回文档字符串中的测试用例集合DocTest;DocTestRunner控制执行DocTest中每一个测试用例,同时调用OutputChecker检查用例的输出,最后统计并输出测试结论。

1.2 doctest测试示例

(1) 描述测试用例

例如对于求取n阶乘的函数factorial(n),设计测试用例对其进行测试。程序结构,如图2所示。

图2 包含doctest测试用例的程序结构

程序上部的文档字符串描述了对factorial(n)进行测试的测试用例,程序下部为factorial函数的定义。

测试用例的设计上,根据阶乘定义factorial(n)=1*2*…*n,可选取n≥1的正整数作为测试用例,如n=10时,阶乘为3 628 800。另外对于特殊情况n=0时,阶乘为1,这一点经常有同学出错,这个特例也可作为一个测试用例。每个测试用例中,三个右箭头(>>>)起始的行表示待执行的Python语句,随后的行表示期待的正确输出结果,格式与在IDLE或Python Shell中执行Python语句类似。

(2) 执行测试

上述程序代码存储在文件example.py中。使用doctest对其执行测试的命令如下。

import doctest, example

doctest.testmod(example)

第1句导入测试工具doctest和用户程序example.py。example中不仅包含了阶乘函数的定义,而且包含了对其进行测试的两个测试用例。第2句调用doctest.testmod函数对example模块进行测试。测试过程如1.1节描述,包含发现和分析测试用例,执行测试用例,返回测试结果等步骤。

(3) 返回测试结果

假设编写的factorial函数没有考虑0的阶乘这种特殊情况,第1个测试通过,第2个测试失败。返回的测试结果,如图3所示。

图3 测试结果

测试结果整体分为两部分,上部为对每一个测试用例的详细描述,如果测试失败,还会给出程序实际输出的错误结果以及期待的正确结果;下部为整体测试结论,包含测试数目以及测试失败的数目。

2 doctest在程序自动评测中存在的不足

doctest虽具备较完整的程序测试功能,但程序测试与教师对学生程序的评测这两者概念不完全相同,将doctest应用在Python课程的作业和考试评测环节中时,仍存在一些不足,主要体现在以下三个方面。

(1) 测试用例的部署。doctest中的测试用例与被测试代码在同一个文件中。如果在学生编写程序的文件中提前设置好测试用例,由于学生可以看到测试用例,会降低考查的难度甚至失去考查的意义。因此需要在获得学生提交的程序之后,动态部署教师设置的测试用例,然后再进行评测。

(2) 测试用例无权重设定。doctest测试用例中没有权重属性,这将无法区分不同测试用例的重要程度。然而在教学实践中,教师往往会根据授课内容的先后顺序、难易程度和课程目标等,调整不同知识点对应的分值。对应到程序评测中,则需要能够对较重要的测试用例设置较高的权重,而对相对次要的测试用例设置较低的权重。

(3) 评测结果难于理解。doctest的测试结果缺乏测试用例的编号项,基于文本的输出形式布局过于简洁,对初学程序的学生来说难于理解。教学实践中,需将评测结果修改为HTML格式,每行描述一个测试用例,方便学生查看,并易于和其它Web教学系统结合。

针对以上三点不足,笔者尝试通过扩展doctest对象模型加以解决。通过扩展DocTestFinder类实现测试用例的动态部署;通过扩展Example类和DocTestParser类实现测试用例权重的设定;通过扩展DocTestRunner类实现评测结果以HTML格式进行输出。

3 扩展doctest实现程序自动评测

3.1 动态部署测试用例

为了能够动态部署测试用例,并且对题库题目及测试数据进行管理,笔者扩展了doctest对象模型,实现了测试数据远程存储和访问管理。相关功能结构,如图4所示。

图4 测试数据的存储和访问管理

首先在远程服务器端搭建了MySQL数据库,建立了TestBank表,用于存储题库数据,字段ID、Topic、DocStr和Answer分别表示题目编号、题目描述、测试用例和标准答案;另外在服务器端设立了基于Python Flask框架的Web服务,提供了Add、Delete、Update和Get等接口,分别用于向数据库添加、删除、更新和获取题目数据;最后在doctest中增加了WebTestFinder和WebTestManager两个类,WebTestFinder用于替换DocTestFinder,其功能为根据题目编号获取远程的测试用例,WebTestManage则根据题目编号对远程题库中的数据进行增加、删除和修改等维护性操作。

学生程序评测时,doctest通过扩展后的WebTestFinder自动从远程数据库中获取测试用例,实现了测试用例的动态部署。

3.2 测试用例权重设定

为了给测试用例增加权重设定项,主要在以下三个方面对doctest做出修改。

(1) 存储测试用例权重。实现思路为扩展测试用例父类Example建立子类MyExample,子类中增加Weight属性,用于存储每个测试用例实例的权重。

(2) 修改测试用例格式。原测试用例由两部分组成:Python语句和输出结果。为了能够描述测试用例权重,笔者约定,在Python语句之前若出现#Weight=N形式的行,则N表示此测试用例的权重。如下方示例,该测试用例的权重为3。

#Weight=3

>>>factorial(10)

3 628 800

(3) 分析识别测试用例权重。实现思路为扩展父类DocTestParser建立子类MyDocTestParser,子类中重写了识别测试用例的parse方法,其中用于识别测试用例的正则表达式从二元组(语句-结果)修改为三元组(权重-语句-结果)。

3.3 HTML格式的评测结果

将doctest的评测输出结果由文本格式改为HTML格式,其思路如下。

(1) 扩展父类DocTestRunner建立子类MyDocTestRunner,子类中设立Results列表,存储测试结果数据,每个Result包含测试用例、实际运行结果和是否通过测试三个属性;

(2) 子类中重写summarize方法,功能为循环遍历Results列表,按照HTML表格(Table)的语法格式总结输出评测结果。表格中每一行展示一个测试用例的评测信息,包括编号、Python语句、正确结果、实际结果、权重和是否通过测试六列数据。

3.4 改进后doctest测试示例

仍以阶乘函数factorial(n)为例,按本文方法对doctest扩展后,测试用例采用后期动态部署方式进行设置,如图5所示。

图5 动态部署测试用例

学生在编写程序和提交程序时看不到测试用例,系统接收到学生程序后,按照题目编号搜索与之匹配的测试用例,将两者组合后再进行测试。

这里,测试用例采用三元组的形式,加入了权重项。改进后的评测结果改为HTML格式,便于查看和数据统计,如图6所示。

图6 HTML格式的评测结果

4 总结

Python标准库中的doctest模块通过文档字符串中的测试用例实现程序的自动化测试,测试功能较完备,内部编程模型和评测思路对于教学实践环节中Python程序自动评判方法的研究具有较高的参考价值和指导意义。本文深入研究了doctest的内部对象模型,通过实例讨论了doctest的测试步骤,分析和总结了doctest的缺点与不足,并针对这些不足之处提出了相应的改进方案,通过扩展doctest编程模型实现了测试用例的动态部署、测试用例权重设定和评测结果的格式化输出。

2019—2020学年第1学期,在我校19级经济和会计两个专业(6个班,180名学生)的Python程序设计课程中,应用了以本文方法为核心设计实现的Python程序自动评测系统,对上机实践环节学生编程作业进行全自动评测,结果显示该系统能够灵活管理和部署测试用例,评测准确、效率高,长期运行稳定,满足了较大规模场景下教学和考试的需要。

猜你喜欢
子类测试用例评测
次时代主机微软XSX全方位评测(下)
回归测试中测试用例优化技术研究与探索
次时代主机微软XSX全方位评测(上)
卷入Hohlov算子的某解析双单叶函数子类的系数估计
基于SmartUnit的安全通信系统单元测试用例自动生成
Java面向对象编程的三大特性
攻坡新利器,TOKEN VENTOUS评测
Java类的继承
Canyon Ultimate CF SLX 8.0 DI2评测
基于依赖结构的测试用例优先级技术