1、JUTA一个Java自动化单元测试工具严俊JUTA:个Java自动化单元测试工具严俊1郭涛2阮玄跻峰3(中国科学院软件研究所北京100190)仲国信息安全测评中心北京100085)(大连理工大学数学科学学院辽宁大连116024)(junyan acm.or)JUTA:An Automated Unit Testing Framework for JavaYan Jun 1 .Guo Tao2,Ruan Huil .and Xuan Jifeng31 (Institute ofSoftware.Chinese Academy ofSciences,Beijing 100190)2(China I
2、nformation Technology Security Evaluation Center.Beijing 100085)3(Schoolof Mathematical Sciences,Dalian University of Technology.Dalian.Liaoningl 16024) Abstract Testing is very important and time consuming in the development of high-quality softwaresystems.This paper proposes an automatic testing t
3、ool JUTA for unit testing of Java programs.The approach is based on sharp analysis of the programs.JUTA firstly employs the Java optimization framework Soot to parse a single Java method into byte code and translates it into a control flow graph(CFG).It then performs depth-first or breadth-first sea
4、rch on the CFG to extract paths from it.Sometechniques such as path length restriction are used to prevent path number explosion.Finally JUTA analyzes the paths based on the combination of symbolic execution and constraint solving.The goal ofpath analysis lies in two folds.lt can generate a set of t
5、est cases satisfying the test criterion such as statement coverage.The test set typically has small number of test cases that are all executable.In addition to test generation for dynamic testing.it can also be used in static testing.JUTA can reveal certain kinds of errors from the source code autom
6、atically if the user provides proper assertions to describe the errors.Thc experimental results show that this tool is efficient for both dynamic and static testing.Key words Java unit testing;dynamic testing;static testing:program analysisisymbolic execution 摘 要 描述了一个Java自动化的单元测试工具JUTA.JUTA首先调用工具So
7、ot解析单个 Java方法 的源码,并将源码解析成一个控制流图在此基础上採用符号执行的方法分析控制流图上的 路径.工具能够自动地产生满足覆盖率标准的程序的测试用例这种方法产生的所有测试用例都是可执 行的,并且一般来说具有较小的测试用例数如果用户能够合理地给岀描述程序错误的断言,框架JUTA 能够自动地检查源码中部分特赵类型的错误实验结果表明工具对Java单元代码的动态测试和静态 测试均能在可接受的时间内给岀有效的结果.关键词Java单元测试;动态测试;静态测试;程序分析;符号执行中图法分类号TP311的控制转移都得到了覆盖)由于在控制流图模型 中,语句与节点相对应,语句覆盖意味着测试路径覆 盖
8、所有的节点同样地,分支覆盖就是所有的测试路 径覆盖了控制流图中所有的边.1.2基于路径的程序测试程序中的一次完整的执行过程对应控制流图中 的一条完整路径但是,并不是所有的完整路径都代 表程序中的一次运行过程这是因为可能不存在输 入数据使得程序按照该路径执行这样的完整路径 称为不可行路径(infeasible path),否则称为可行的 (fcasiblc)2.例如,在例1中,路径pl执行的语句 如图2所示(符号后的表达式表示程序执行该 逻辑判断为真的分支)易知,程序执行到第2个判 断语句时,表达式(i+2Xj2)&(j3);J=j-U(i+2Xj5);good=FALSE;Fig.2 Path
9、pl.图2路径pl由于控制流测试准则都是基于控制流图中的测 试路径来立义,很自然地,我们可以采用如下方法来 生成测试用例:首先从控制流图中找到一组路径满 足覆盖准则,然后从每条路径中找岀各变量的一组 初始值使得程序能够按照路径执行,这些初值就构 成了测试用例集合一般来说,一个自动测试用例生 (automated test case generation)系统主要可以分成3个部分:1 .程序分析器(program analyzer),其主要工 作是把程序转化成容易处理的中间模型(一般就是 我们前而提到过的控制流图);2.路径选择器(path selector),如何选择路径 满足我们的测试需求(
10、比如覆希准则);3.测试数据生成器(test data generator),如何 从测试路径中得到我们需要的测试数据. 在实际开发中,程序分析器也被称为前端 (frontend),B两部分合起来被称为后端(backend). 这样的自动测试生成的系统主要用于程序的动 态测试,即采用测试用例测试被测程序.与动态测试 对应的静态测试方法,其框架与测试生成大体相同. 主要的不同指出在于其输出并不是测试用例,而是 直接报告程序是否包含某类错误,或者给出能检岀 某类错误的具体的测试数据.由于程序测试的需求众多,实际的测试系统尤 其是商用系统,可能还包括一些其他的辅助模块,比 如测试脚本的自动生成器等.
11、13路径分析方法不可行路径给自动化测试带来了一个难题,我 们不可能简单地通过从程序控制流图的信息中抽取 路径来选择测试用例,在测试用例选择时必须分析 数据流的信息为了精确地分析控制流图中一条完整 的路径,我们可以采用符号执行(symbolic execution)4 的方法所谓符号执行,相对于普通的程序执行来 说,就是程序并不真正用数值来替换程序的变量,而 是用一组关于初始值的表达是来表示程序中岀现的 项用符号执行加约朿求解进行程序分析的基本思 想是:采用Hoare逻辑可以将程序路径表示成P QR,其中P是程序的前宜条件(pre-condition.执 行程序前需要满足的条件,或者称为前断言)
12、,R是 程序的后置条件(post-condition,程序执行后需要 满足的条件,或者称为后断言)假立在程序的符号 执行过程中由P)Q可以推导出约束条件clAc2A Acn,则应该有clAc2A在一般的测试 中,如果后断言永真,那么可以对约束条件clAc2A- Acn求解,可以得到变量的初值,从而得到测试用 例.另一方面,我们可以对约束clAc2AAcnAJ R求解,如果有一组解满足这一约束,说明存在一组 输入使运行程序的结果和规范不符如果程序的规 范正确,则程序中必定包含错渓.对于不包含函数调用代码来说,程序的语句 可以分成3类:1)动作(action),也就是赋值语句;2)判断语句(pre
13、dicate),也就是一个逻借表达式;3)其他不影响变量的表达式,如控制台打印语句 System.out.println .我们用一个具体的例子说明符 号执行的原理考虑图2的路径pl,如果i和j的初 值为iO和jO.我们可以自顶向下依次执行这段程 序,用程序的赋值语句替换判断语句,最后得到一个 只含有判断语句的约束集合iO+2X(jO-l)2)&(j03) 这个集合就是程序初值的约朿由于这个约朿集是不可满足的,所以路径pl 是不可行的.关于符号执行的具体细肖请参考文献 2,5-6.符号执行的好处是能够精确地分析程序的行 为,但它的缺点也是显而易见的.由于符号执行方法 过于依赖源代码,并且需要消
14、耗较大的汁算资源,因 1842计算机研究与发展2010.47(10) 而无法精确分析带有很多模块调用(特别是当这些 子模块代码不可见的时候)的程序.同时,程序需要 能够用约朿精确模拟复杂程序结构比如指针和数 组的执行,这是一件很困难的事情最后,为了保证 分析的精确性,符号执行需要有一个能力非常强的 完备解法器(complete sol ver,就是说,只要约朿有 解,解法器必然能够给出一组解).针对不同的程序, 这个解法器可能需要处理位操作等复杂的约束这 一点也限制了符号执行方法的使用工具JUTA中 采用较为简单的方式在工具的处理能力和精确度之 前作了一个平衡我们将程序中的表达式限制为布 尔逻
15、辑和线性数值约束两种类型,并且将程序的外 部调用抽象成简单的赋值语句和后置断言.具体内 容见本文第2节.2工具JUTA的设计与实现本节将详细介绍JUTA工具的流程以及工具 的模块组成,同时对每一个模块给出英功能以及简 要的描述,主要目的是为工具提供一个整体的框架 描述.2.1工具流程 为了便于理解源程序需要对程序的源代码进 行一圮的处理.首先要做的就是编译分析,包括语 法语义错误的检查、建立中间表示(intermediate representation,IR)并生成控制流图等.同时为了以 后的分析,我们还需要识别出源代码中的循环,并对 循环进行处理,建立一棵循环控制树这部分通常成 为前端处理
16、.有了前端处理的程序结构和数据信息, 我们就可以通过算法遍历控制流图,得到一系列的 路径,并通过符号执行和约朿求解来分析这些路径, 对其可行性进行判断,对于可行的路径为其生成测 试数据,最后收集这些测试数据就得到了满足某些 性质的目标测试数拯集因而,从整体上工具分为两 个部分:前端处理和后端分析,英整体结构如图3 所示:Fig. 3 Framework of J UTA. 图3 JUTA的框架目前,工具JUTA实现了两方面的功能:静态 测试与动态测试本文1.2节曾指出,这两种测试方 法可以公用相同的前端,主要区别在于工具的后端. 接下来我们将按照功能分别介绍工具的各模块. 2.2工具输入 工具
17、输入包含两部分.首先是被测的Java源 码.源码中包含被测Java代码的单个方法由于工 具JUTA的主要功能是完成单元测试,所以,工具 将丢弃方法中的外部凋用用户在测试时,需要手工 将重要的外部调用替换成赋值语句或者约束表达 式.例如,对于语句 y=foo(x), 假泄外部调用foo将y的值改变为foo x,并且这个 值的范由是lb.ub那么可以将这行代码替换为: y=foo x;assert(Ib =foo x&foo x0)y=i;elsey=2;returny;public static intfunc(int) intx,y,temp $ O.tcmp $ 1;x:= paramete
18、rO:int;y=-Uifx0 goto labelO; goto label 1;labclO:nop;temp $0=1; y=tcmp $ 0;goto label2; label knop;temp $ 1=2: y=temp $ 1; label2:nop;retumy;)(a)(b)Fig.4 Java source code and the corresponding Jimple code.(a)Java source and(b)Jimple code图4 Java源码与对应的Jimple代码.(a)Java源码;(b)Jimple 代码2.3.3控制流图建立模块本模块通过遍
19、历中间表示的列表,将中间表示 序列划分为基本块,控制流图即是以基本块为节点 的有向图,描述了源代码中的控制流工具JUTA 使用Soot中的Brief Block Graph,只为源代码中基 本的控制流提供支持,目前无法处理异常(exception). 2.3.4循环控制分析模块这一部分主要是分析控制流中的循环,Soot中 将每一个循环抽象成一个对象,每一个循环都存储 自己的循环头部以及循环体.一般来说,结构化的程 序中的循环分成3类,孤立循环(single loop).嵌套 循环(nesting loop)以及串联的循环(concatenated loop)8.我们可以建立一个抽象的循环控制森
20、林, 森林中每一个节点是一个表示循环的对象,用来描 述循环之间的关系循环A是循环B的子节点,当 且仅当循环B内嵌在循环A之中.当被测程序中不 包含串联循环时,循环控制森林退化为循环控制树. 2.4后端分析 本节将介绍工具的后端.考虑到动静态测试的 不同之处.我们将分別介绍动态和静态测试的功能 模块,之后对两者进行一个比较.2.4.1动态测试后端动态测试的目标是生成一组满足覆盖率的测试 数据所以,动态测试的后端需要分析前端处理后得 到的数据信息(包括中间表示、控制流图以及循环控 制树),根据路径产生策略以及覆盖准则产生满足要 求的路径,并且通过分析路径的可行性得到测试数 据这一部分有以下4个模块
21、:路径生成、路径转换、 路径分析以及路径选择.1)路径生成模块这一模块将分析由前端处理得到的控制流图, 并抽取出一系列的路径.这些路径可以用基本块的 编号序列表示路径的产生过程是通过遍历控制流 图来得到的由于程序中可能有循环,那么,从图中 可能抽取出无限条路径.为了产生有限条测试路径, 我们可以限制循环次数或者限制路径的长度(路径 表示中基本块的个数)在工具JUTA中可以通过 参数来选择使用的路径限制策略.路径产生是一个遍历控制流图各顶点的过程, 在图的遍历算法中有两种常见的策略:深度优先遍 历(DFS)和宽度优先遍历(BFS).当采用BFS算法 时,是按照路径长度由短到长的次序生成路径,所以
22、 在队列中需要存储最多两层的中间ij点,当路径长 度限制比较大或者循环次数限制值比较大时,队列 中需要存储很多的肖点在使用DFS算法时,由于 优先考虑循环的退出节点,在栈中存储的元素数目 比较少,但是生成的最终路径不是按照由短到长的 顺序而且很多路径都是由几个相同的基本块所组 成由上述的比较可知,使用DFS占用较少的系统资 源,但是在处理过程中,尤英是在CFG包含环时容易 陷入一个分支无法跳出,无法快速达到覆盖率指标, 比较适用于静态测试;使得BFS能够较快达到覆盖 率,但是代价相对较髙,适用于在测试覆盖准则指导 下的测试生成.工具JUTA的路径生成模块实现了这 1844计算机研究与发展201
23、0,47(10)0引 言软件质量历来就是学术界和工业界共同关心的 问题,因为软件质量问题造成巨大经济损失事件不 胜枚举当前,随着信息技术的迅速发展,特别是互 联网技术的广泛应用Java技术以其通用性、平台 移植性、安全性等特征,吸引了众多软件开发者,在 各个重要的行业部门得到了广泛的应用因此,如何 提高软件质量,尤苴是提高应用广泛的Java程序的 可靠性,长期以来一宜是一个非常重要的研究课题. 软件测试是提髙软件质量的重要手段优秀的 软件开发机构把40%的工作量花在软件测试上,软 件测试费用则占到了软件开发总费用的30% 50%,而对于一些要求高可靠性、髙安全性的软件, 测试费用已经抬高到了整
24、个软件项目开发所需费用 的35倍.单元测试是软件测试的早期阶段,这种测试方 法主要测试程序的单元函数或者类中的方法,苴主 要原理是对源代码中的控制结构和处理过程等进行 分析,检查程序内部处理是否正确,包括语句结构、 分支和循环结构等由于单元测试需要分析源代码, 所以测试的代价往往比较髙目前,在工业界,单元 测试大多仍然是靠人工完成的,如手工设计测试用 例、人工审阅代码等,不但十分繁琐,而且测试质量 与测试人员的主观经验紧密相关随着计算机软硬 件规模的不断发展,以人工为主的测试方法已经无 法满足测试的需求.目前,采用汁算机辅助的自动化 软件测试技术成为软件工程中一个活跃的研究 领域.本文介绍一个
25、Java自动化的单元测试工具 JUTA(an unit testing and analyzing tool for Java). 与Junit等传统测试框架不同JUTA提供了一系 列的精度和自动化程度较髙的技术来生成测试用例 以及检查代码中的错误,以提高测试的自动化程度 和效率.1预备知识本巧将简要介绍JUTA采用的控制流和数据 流分析技术.1.1控制流图形式化地说,控制流图(control flow graph, CFG)是一个有向图G= N,E,s,f淇中N代表 节点的集合.E代表边的集合,s和f分别表示控制 流图的起点和终点图中每个点nGN代表程序中 的一系列顺序运算(即这些语句在程序
26、执行过程中 要么全部都执行,要么全部都不执行),或者说是一 个基本块(basic block);而每条边,e=(ni,nj)GE, 代表从肖点ni到nj的一个转移用流图作为程序结 构的模型时,程序的运行过程可以用流图中的路径 来刻画控制流图中的一条执行路径p代表一个执 行序列p= nl,n2,nm ,其中m是路径的长度, (ni.ni+1) G E( 1 Wi2)&(j3)(*S2* j-;*S3* if(i+2*j5)*S4* good=FALSE;returngood;)Fig.l A piece of example code. 图1 一段示例代码对于基于控制流图的结构测试来说,主要的测
27、 试方法就是找到一系列的可行测试路径,这些路径 的输入数据就是测试用例例如在例1中,i=3, j=4就是对应于路径p2的一个测试用例.测试人 员通过执行这些测试用例来检査程序的错误显然, 由于程序代码的复杂性,通过枚举所有的测试路径 来测试程序是不现实的.为了减少测试的代价,人们 定义了一系列的测试覆盖准则(coverage criterion), 作为测试的标准来限制测试集的大小1.最简单的 控制流覆盖准是语句覆盖(statement coverage卩 在软件测试过程中,只有当程序中的所有语句都得 到了运行,才能称该测试是充分的)和分支覆盖 (branch coverage,即要求在软件测
28、试过程中,所有 1841严 俊等:JUTA:个Java自动化单元测试工具 两种策略,用户也可以用参数指泄所使用的遍历策 略在默认情况下使用的是深度优先.2) 路径转换模块前面1.3节中介绍过,对于不包含函数调用代 码来说,程序的语句可以分成3类.而我们主要关注 其中两类语句,即赋值和判断路径转换模块将采用 基本块的编号序列表示的路径,根据基本块对应的 Jimple中间表示.翻译成一些简单的赋值和判断语 句,方便下一步的路径分析.3) 路径分析模块这一部分主要是用来对产生的路径进行分析. 张健等人在文献2,5-6中提出了基于符号执行以 及约束求解的路径分析方法,并给出了一个工具 EPAT用于判断C程序路径的可行性.目前,EPAT 支持布尔逻辑表达式和线性数值约束表达式及苴混 合,同时还支持数组和指针下标的运算.考虑到经过断言插入和路径转换后的路径只包 含两类基本的表达式:判断和赋值,从而可以调用 EPAT来分析Java程序的路径条件,判断出路径是 否可行,并对可行的路径产生测试数据.4) 路径选择模块为了采用较少的测试数据测试程序,可以在所 有可行路径中选择一些有代表性的路径来测试被测 代码.可行的选择标准就是覆盖准则,例如
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1