基于MVC模式的Web系统自动化单元测试方案
2016-03-25张敏陈静王娟
张敏,陈静,王娟
基于MVC模式的Web系统自动化单元测试方案
张敏,陈静,王娟
摘 要:随着网络技术的不断发展,Web应用的质量和可靠性变得越来越重要,如何对Web应用提出一套完整的测试方案保证Web应用的质量成为当前迫切需要解决的问题。针对基于MVC模式的Web应用系统,提出了一套全面的自动化单元测试方案,确定了一套实用的单元测试流程,并按照提出的方案和流程对系统进行测试,通过重构解决发现的问题,改善了系统的性能。
关键词:单元测试;自动化;JUnit
0 引言
随着网络技术的不断成熟以及人们对网络需求的多样化发展,Web应用越来越普及并日益复杂,如何构建快速、安全、健壮的Web应用系统已经成为亟待解决的问题。
在快速开发Web应用时,测试作为软件工程中极为重要的一个角色常被忽视。由于单纯地认为测试工作耗费时间,商业效益不显著,许多开发人员甚至企业摒弃了宝贵的软件工程原则,在完成了项目的分析、设计、开发后才考虑开展测试工作。尽管开发人员在编码过程中煞费苦心,代码质量却往往和预期相反,系统不稳定、不健壮,这些因素又减缓了开发过程,增加了开发成本。因此如何用高效率创造高质量的Web应用系统,保证系统的正确性和可靠性已经受到重视;Web测试作为Web应用系统开发过程中的一个重要环节,如何有效、系统地开展该项工作是保障系统可靠性的关键。
单元测试在Web测试中发挥着至关重要的作用,由于单元测试的对象是可独立编译或汇编的程序模块,单元测试的目的是检查每个软件单元能否正确地实现设计说明中的功能、性能、接口和其他设计约束等要求,发现模块内可能存在的各种差错,因此及早地进行单元测试可以快速适应需求变化,避免产生大量错误,提高系统性能。针对目前常用的基于MVC模式的Web应用系统,本文提出了一套通用的自动化单元测试解决方案,并以该方案为指导,构建了针对Web应用系统的自动化单元测试系统,发现了系统在开发过程中存在的问题,通过对系统部分重要功能的重构有效地解决了问题,改善了系统性能。
1 单元测试方案
单元测试包括程序逻辑性测试、集成测试、功能测试。总体测试方案从局部到整体展开,是一个典型的MVC交互的结构图,如图1所示:
图1 整体结构图
用户可以通过MVC模式实现查询、修改等交互操作。MVC模式分为3个模块,分别是模型(Model)、控制器(Controller)、视图(View),并通过模型模块和数据库模块进行交互,因此测试方案相应地划分为模型测试模块,使用Junit对Java Bean进行测试;控制器测试模块,使用Cactus和HttpUnit对Servlet进行测试;视图测试模块,使用HtmlUnit和JsUnit对Html,Jsp和JS进行测试;数据库测试模块,在对数据库模块进行测试时,常使用模拟连接、构造数据和验证数据等方法。
在对每一模块进行测试时,根据模块中的源文件构造相对应的测试文件,通常为一对一的关系,即一个源文件对应一个测试文件,在一个测试文件中,按测试粒度由小到大的规则,首先进行程序逻辑性测试,粒度大小为一个函数,针对文件中每一个函数编写对应测试函数;功能测试关注的是程序某一功能的实现是否正确,相对函数的测试粒度增大,因此在程序逻辑性测试之后进行;在集成测试时采用增量式方法,由若干函数增加至一个文件,由一个文件增加至若干文件,最终将所有的测试文件进行集成测试。增量式集成测试最好伴随程序逻辑性测试和功能测试进行,通过不断地集成测试,从总体上提高了测试的质量和效率。
因此,本文提出的测试思想和方案具有一定的通用性,测试的整体思路是首先由大到小划分测试模块为若干测试文件,最终划分到函数粒度,在函数粒度级别完成逻辑性测试,覆盖代码及功能分支,再由小到大增量完成集成测试,覆盖功能性测试。通过这样的测试思想,使得系统由代码分支到系统功能被充分测试,及早充分发现系统中存在的问题,提高开发效率,有效地降低开发和后期维护成本。
1.1 用JUnit实现单元测试
JUnit是由Erich Gamma和Kent Beck共同为编写单元测试发布的框架,用于编写和运行可重复的测试用例。它提供了丰富的断言机制测试期望结果,可方便地组织和运行测试套件,在极限编程和重构实现中发挥着重要作用。在实际测试中,对每一个Class编写对应的测试文件,Class中每一个方法对应一个测试函数。当Class的功能变动时,只需修改对应的测试函数,如果是对Class中的代码进行重构,即改变代码不改变功能,则不需修改测试文件的内容,通过不断重构和执行测试用例来改善代码质量。在编码和测试的过程中,必须确保所有重要代码都被测试,有多种测试覆盖策略,例如按代码行数覆盖、按分支覆盖等。
1.2 对数据库进行测试
采用JUnit对数据库进行测试时,检查相关输入数据,传统且简单的方法就是用if语句与期望值进行比较,在JUnit中,用断言来代替大量的if语句。断言是另一种将期望值与实际值比较的方式,如果比较的结果相等,则测试用例通过,否则失败。在JUnit中,将初始的变量或者对象放在setUp()中,它是独立于断言的,因此同一个变量或者对象可以服务于多个测试用例;当测试结束,将销毁变量或者对象放在tearDown()中实现。
在对数据库进行测试时,首先验证是否正确建立连接,该过程需要连接真实的数据库完成;由于在真实环境中进行测试易产生错误、降低测试效率,因此最好模拟Connection、Statement、Resultset测试访问语句是否正确,可以用工具EasyMock实现模拟过程;开发人员在编写代码对数据库进行操作时,经常忘记释放资源,而JDBC API不自动回收资源,会导致由资源泄漏引起的众多问题,因此需要测试JDBC资源是否释放。
数据库测试最常见的方式是向表中插入一条记录,然后进行验证,最后删除该条记录。若多个测试共享同一个数据库,在这种并发状态下,数据库处于变动之中,导致多个测试之间相互影响,可采用以下方法解决该问题:一是使用回滚技术,将从插入到删除的过程作为一个整体,如果被中断或出错就进行回滚;二是在setUp()中实现数据库连接和数据初始化,然后在测试函数中插入记录并进行断言,最后在teardown()中删除记录并释放JDBC资源。
1.3 对服务器组件进行测试
Cactus是JUnit的扩展,可方便地实现对服务器端组件的测试,包括Servlet,EJB,Taglib,Filter等。Http Unit是一个集成测试工具,要有两个功能,一是作为提供发送请求和接收响应功能的客户端,通过模拟浏览器的行为与服务器端进行交互并处理返回的表格表单等;二是简化了验证响应内容的方法、简化断言。
在本文提出的方法中,采用Cactus与Http Unit相结合的方式对服务器进行测试,用Cactus实现模拟服务器操作过程,用Http Unit进行断言可以精确定位表单返回值。在MVC模式中,基础服务器组件是Servlet,常见测试方法是对Servlet的doGet()和doPost()方法进行测试,此外,还需对Request进行测试,验证接收的值和预期的值是否相等。为了测试Servlet是否跳转至正确的页面,可通过抓取目标页面的内容和预期的相比较来确定。
1.4 对视图进行测试
View的测试包括验证JSP页面的正确性以及对JavaScript文件的测试。本文分别采用HtmlUnit和JsUnit完成。如同在浏览器中的操作一样,HtmlUnit提供了一个接口允许调用网页,填写表单,点击链接等。JsUnit是一个支持客户端JavaScript脚本的单元测试框架,使用它对JavaScript编写的函数进行行为测试。在验证JSP页面的正确性时需要查找空链接,本部分详细介绍一种用HtmlUnit实现的算法(算法名称:checkLink),可以有效地查找到存在的空链接,算法的关键步骤如下:
第一步:调用WebClient.getPage(URL root)进行页面检索,root为主页地址,本系统中为典型案例的主页地址。
第二步:如果该页面是HtmlPage,将得到所有的<a>标记,并递归跟踪每一个链接,使用HtmlAnchor中的click方法判断链接是否有效,如果有效,将已检查过且有效的链接添加至一个ArrayList中并继续跟踪,否则跳转至第三步或第四步。
第三步:如果链接到一个域外的页面,则终止该级的链接检查,返回上一级页面继续。
第四步:当跟踪某个链接出错时,则判定该链接失效,将失效的链接添加到一个ArrayList中,终止该级的链接检查,返回上一级继续执行。
为了提高程序执行的效率,需要增加判断方法,对已经检查过的url地址和本页中的链接即以“#”开头的地址,不做重复性的检查,否则就会产生循环递归的问题,最终导致资源耗尽。对不同参数但指向同一页面的地址,采用“?”作为判定条件。
2 实验研究
本文提出的单元测试方案适用于基于MVC模式的Web应用系统,并已有效地应用在实际系统的测试中,通过对一系列Web应用系统进行测试,发现系统中存在的问题并通过重构解决,提高代码质量,使系统更加高效稳定运行。
2.1 Web系统存在的问题
在对实际系统进行单元测试的过程中,除了发现各系统实际存在的Bug外,还发现Web系统在开发过程中通常会出现的一些问题。这些问题虽然本身不是Bug,但它们的存在会带来比Bug更严重的问题,多数Bug可以快速修复,使得系统功能恢复正常,但是在开发过程中,由于不良设计、不良开发习惯和水平低下的开发人员等原因导致的问题不仅严重影响系统的功能和性能,还使测试模块难以编写,影响测试效率,从而降低系统开发效率,提高开发和维护成本,降低系统可靠性。现将开发过程中最容易出现的几类问题归纳如下:
1)源文件中单个函数实现功能过多。这样会导致测试不灵活,函数中的所有测试点只能同时进行,由于测试粒度大,不利于定位出错地方,还会增加对环境的依赖。例如,一般在实现Servlet功能的初始阶段,可能会把所有功能都放在一个函数中,随着Servlet功能的增多,函数功能及函数代码相应增多,这样就导致单元测试函数功能复杂且单元测试代码粒度过大、难于维护和定位错误、可读性差等问题。在具体实现时,如果doPost()函数功能过多,代码过长,不利于编写测试用例,即使测出问题也难以确定错误模块,降低开发效率。
因此,通过函数单元测试发现的问题可以总结出,在开发时,函数功能的设计及开发尤为重要,开发首要的原则就是功能独立,函数粒度不宜过大,可以通过减少单个函数功能、提取公共代码等方法实现。
2)源文件代码冗余,包括变量冗余和函数冗余。这不仅使编码容易出错,修改不方便,同时也增加测试难度和测试量。例如,数据库的连接和释放的代码多次出现在不同的方法中,或者实现对数据库进行插入、删除时,由于表名等名称不同而导致冗余,在这种情况下,如果需要调试错误(如将没有释放的数据库连接关闭),则会涉及到多个方法,漏改的情况容易出现。因此导致代码可维护性和可读性差,在开发阶段如出现需求变更或者人员变更,则代码的维护工作会变得异常复杂。
3)过于依赖框架。除了对数据库的依赖之外,还存在对Web容器的紧密耦合。这样的设计会使代码逻辑不清晰,降低代码质量,给阅读、修改带来困难,且无法进行模拟测试,导致测试只能在真实环境下进行,在测试过程中需要反复调用数据库、Web服务器,浪费资源,且在大数据量的情况下,效率非常底下。
2.2 通过重构解决问题
针对在测试中发现的系统存在的问题,提出了以下解决方法,并通过重构完成。
1)明确函数的功能,拆分较大模块函数成为大小适中的函数,粒度过大不利于测试,粒度过小会使得开发和测试过程变得繁琐,增加开发和测试成本。例如在对Servlet重构时,本着极限编程及测试驱动开发的思想,应针对不同的功能编写测试函数,将doPost()函数拆分成若干函数以实现不同功能,尽量保证各函数之间低耦合高内聚的特性,当所有测试用例通过时,每一个函数实现了预期的功能,在保障功能正确的同时提高了代码质量。
2)针对程序中存在的大量冗余代码,分析代码功能,将相同功能的代码通过重构抽取成公共方法封装在类中,供其他程序调用。例如,可以写一个数据库操作类,对数据库操作进行合理的封装,避免数据库操作的重复代码,实现了代码的重用性。
3)为减少对框架的依赖,业务逻辑尽量从容器中抽取出来,例如,针对业务逻辑依赖数据库访问层接口的问题,可以创建另一个类实现这个接口,实现与数据库交互。通过业务逻辑与持久框架的分离,可以使用预定义的数据对业务逻辑测试。
按照高耦合、低内聚的原则,根据上述发现的问题及相应的解决方法,通过对系统中重要功能进行重构,减少了代码冗余,改善了代码质量,提高了系统性能和稳定性。
3 总结
本文提出了一套单元测试方案,系统地建立了针对MVC模式的Web应用的测试流程,发现了开发中的问题,提出了有效的解决方法,并将测试方案以及重构思想有效地应用到实际系统中,探索了单元测试在测试驱动开发中的应用,总结了宝贵的经验。本文的特点在于针对一类特定问题提炼出测试思想,形成了一套通用的测试方案,重点总结了测试技巧以及在测试中发现的问题,并通过不断地重构,提高效率,对以后的开发和测试均有指导意义。
参考文献
[1] Edward Hieatt,Robert Mee. Going Faster:Testing The
Web Application [J].IEEE SOFTWARE,2002,19(2):60-65
[2] Gamma E. and Beck K., "JUnit," http://junit.org, 2005.
[3] 徐雯,高建华. 基于Spring MVC及MyBatis的Web应用框架研究[J] 微型电脑应用,2012.7
[4] EasyMock home page. http://www.easymock.org.
[5] Louridas.P JUnit: Unit Testing and Coding in Tandem [J].IEEE SOFTWARE,2005,22(4):12-15
[6] 申华.基于MVC的光照度智能控制监测平台研发[J]信息与电脑,2015.12
[7] 周立力,极限编程的质量保证分析[J]. 计算机应用与软件,2010,27(4):167-168
[8] S Freeman,《测试驱动的面向对象软件开发》[M] 北京:电子工业出版社,2010
Automated Unit Testing Solution of Web System Based on MVC
Zhang Min, Chen Jing, Wang Juan
(Xi'an FANYI University, Xi’an 710105, China)
Abstract:With the development of network technology, the quality and reliability of Web application was more and more important. How to make a complete testing solution for Web applications to ensure the quality was an urgent problem which needs to solve. The article gave a comprehensive automated testing solution for Web application system based on MVC. It made a simple and practical testing process, and did testing for web systems according to the proposed solution and procedure. It could not only find system error, but also summarize problems found in system developing period. And it could solve problems according to reconstruct in order to improve system performance.
Key words:Unit Testing; Automation; JUnit
收稿日期:(2015.12.03)
作者简介:张 敏(1980-),女,赤峰市,西安翻译学院,讲师,硕士,研究方向:数据库与知识库,西安,710105 陈 静(1986-),女,西安翻译学院,助教,硕士,研究方向:信息处理,西安,710105
文章编号:1007-757X(2016)02-0078-03
中图分类号:TP311
文献标志码:A