摘 要
单元测试是集成测试与系统测试的基础,是测试驱动开发与软件重构的基石。
随着测试驱动开发方法的流行,单元测试愈发重要。本文主要研究 Java 程序的单元测试。通过对单元测试理论及工具的研究,发现当前的 Java 单元测试还存在一些问题,这些问题主要包括:当前测试框架不能自动地选择测试用例,不能自动地添加运行测试用例。当需要频繁、有选择性地运行大量测试时,手动方式十分繁琐;测试中使用的模拟框架具有不能模拟 final 类等局限性,并且实现模拟功能的代码与测试的代码混合在测试方法中,不仅影响测试方法的可读性,而且不利于模拟对象的复用;当前测试框架没有为测试方法的复用提供有效的支持;针对以上问题,将单元测试与面向方面编程结合,研究 AspectJ(一种面向方面框架)在 Java单元测试中的应用,取得成果主要包括:
1) 扫描整个项目自动添加测试用例到测试套件,框架自动运行套件中的测试用例。在添加过程中利用 AspectJ 的横切功能横切添加用例的方法,使得只添加在切点中描述的测试用例,实现测试用例自动地选择、自动地添加运行。
2) 利用 AspectJ 切点捕获被测方法对外部的依赖,在切面中改写依赖方法,隔离依赖,并设计了隔离依赖的切面模型。在切面中设置预期的方法调用,被测方法运行时用切点捕获实际的方法调用,在切面中验证两者是否一致,实现交互测试,并设计了实现交互测试的切面模型。
3) 设计了两种复用测试方法的模式:参数化测试与接口测试方法复用。利用AspectJ 切点捕获测试方法中被测方法的调用,在切面中用多组测试数据循环调用被测方法,记录每组的运行结果。捕获测试方法中断言方法的调用,复用断言方法断言每组数据的运行结果,实现参数化测试。利用 AspectJ 的静态横切功能,让各实现类的测试类复用切面中实现的接口中方法的测试方法,实现接口测试方法复用。
为了方便设计的方法在实际单元测试中的使用,设计并实现了一个基于 JUnit的单元测试框架 TestWAJ(Test with AspectJ)。最后,运用 TestWAJ进行单元测试,验证了本文提出的解决方法是可行的。本文实现测试用例的自动选择,自动添加运行,提高了 Java 单元测试自动化水平。设计的两种复用测试方法的模式,增强了测试方法的复用性,减少了测试成本。用 AspectJ 实现的模拟框架,比传统模拟框架功能更强,复用性、灵活性更高。
关键词:单元测试,AspectJ,JUnit,模拟框架,测试方法复用
ABSTRACT
Unit testing is the basis of the integration test and system test and foundation of test-driven development and software refactoring. And as test-driven development grows in popularity, unit testing is increasingly important. This paper mainly studies the Java program unit testing. Based on the research of the unit test theory and tools, found that the current Java program unit testing also has some problems. These problems include: the current test framework can't choose the test cases automatically, also cannot automatically add test cases to Test Runner; When you need to frequently and selectively running a large number of tests, the manual test is very tedious; The mock framework used in unit testing has the limitations such as can not simulate the final class. In the test method, mixing the mock code with the test code not only affects the readability of test methods, but is also not conducive to the reuse of mock object; The current test framework don’t provide effective support for the reuse of test methods. To solve above problems, combining unit tests and aspect-oriented programming, we research the application of AspectJ (an aspect-oriented framework) in Java unit test. The main results are as follows.
1) First of all, scan the entire program and automatically add test cases to test suite, and then the framework automatically run the test cases in test the suites. In the process of adding test cases, Using AspectJ's crosscutting function crosscutts the method that used to add test cases, make the framework only to only add test cases described in the aspect. Realize the function that the test framework can automatically select, add and run the test case.
2) Capture all dependencies on external resources of the tested method using AspectJ’s pointcut. In the asepct, we rewrite the depended method to isolate dependences. And design the aspect model for isolating dependences. We can set expected methods’ invocation in aspect. Then, capture the actual method's invocation using the pointcut when the tested method is running. Then verify the consistent of actual invocations and expected invocations. We realize the interactive test, and design interactive test's aspect model.
3) Two methods of reusing test method is designed. They are the parametric test and the reuse of interface test method. Capture the invocation of tested method in the test method using AspectJ’s pointcut. We use multiple sets of test data to loop calling the tested method, and record the result of A single set of test data. Capture the invocation of assert methods in the test method. Reuse assert methods to assert the result for A single set of test data. Finally, realize parametric tests. We use AspectJ's static crosscutting function to let each implementation class's test class reuse test methods of interface's methods implemented in the aspect. Finally, realize the reuse of interface test method.
In order to facilitate the use of designed functions in the actual unit tests, this paper designs and realizes TestWAJ(Test with AspectJ) based on JUnit. Finally, unit test with TestWAJ, the results show that the proposed solutions are really feasible. This paper realized the function that the test framework can automatically select, add and run the test case. It improve the level of automation of Java unit test. Because of designing two methods of reusing test method, it can enhance the test method's reusability and reduce the test cost. The mock framework implemented by AspectJ is more stronger , and has higher reusability and flexibility than the traditional mock framework.
KeyWords: unit testing, AspectJ, JUnit, mock framework, the reuse of test methods
单元测试是为了保证被测程序的一个很小的功能正确运行而编写的一小段验证代码。单元测试的优点众多,主要包括:1.帮助我们编写代码,预防 bug。我们在编写代码的时候,由于事先对代码的功能考虑不周全,导致遗漏某些 case 或边界条件等问题。如果我们在编写程序前,先为程序写好单元测试,写单元测试的过程中会促使编程人员对代码的功能,实现流程做详细的思考,这样在真正写代码的时候会思路清晰,减少 bug 的引入,从而提高代码的质量。2.能更轻松,更快的反馈代码是否正确的信息,减少我们验证代码的工作量,加快开发效率。
试想我们完成了某个功能或者修改了某个功能,如果不使用单元测试,我们需要启动整个程序,验证新功能是否工作正常。如果是一个 WEB 项目那可能包括发布项目,启动服务器等繁琐的过程。但是,如果使用单元测试,我们可以使用mock框架为程序搭建一个独立的运行环境,只需简单调用新功能,验证输出结果,就能轻松知道是否正确。与之前的方法相比,单元测试能更快,更轻松的提供检测功能,减少我们工作量。3.确保以后对代码的修改不会影响之前已编写代码的功能。随着项目的深入,代码量越来越大,如果此时需要修改先前写好的代码,这些代码恰巧是系统的公共接口或底层的支持类,很有可能牵一发而动全身对系统造成不可预知的影响。这样导致修改需求被搁置或推迟,由于不敢改进代码,代码也变得越来越难以维护,质量也随之变差。而单元测试为这个问题提供了很好的解决方法。由于之前为了保证程序的功能,我们已经编写了单元测试。现在,对代码做修改后,我们只需再次运行原来的单元测试就能知道新的修改是否会影响到系统中其它部分。4.增加代码的可维护性。为代码写单元测试,相当于给代码加上了规格说明书,而且单元测试代码大多逻辑简单,结构清晰,开发人员通过读单元测试代码能轻松理解代码的功能。目前,测试驱动开发[1]
(TDD)在 IT界风靡。它要求在编写实际功能代码之前,首先编写测试代码。也就是说在确定需要开发某个功能后,首先考虑怎样对这个功能进行测试,在测试代码的编写完成后,然后编写有关的功能代码使测试代码通过。再循环增加其他功能,直到所有功能的开发完成。在代码重构[2]中自动化单元测试是保证重构安全性的重要手段,通过单元测试保证重构后的代码外部行为不变。对测试驱动开发和代码重构而言单元测试至关重要。
单元测试与生产代码相同,测试代码同样需要具良好的可读性,可维护性,尽量避免重复。我们在测试方法中使用第三方框架(如隔离框架),这些框架的功能代码和测试代码混合在一起,降低了测试代码的可读性,复用性和可维护性。
如果我们使用 JUnit 框架,测试类必须继承 TestCase,测试方法必须无参无返回值。这些限制使得我们无法通过继承,参数化测试等手段复用测试方法,增加程序员工作量。
不能自动选择运行单元测试,影响了 Java 单元测试自动化水平,增加单元测试的成本;模拟框架的缺陷限制模拟框架的使用范围,模拟代码与测试代码混合,影响单元测试的质量,影响模拟对象复用性。缺少有效的复用手段,增加了测试人员的负担。因此很有必要研究这些问题的解决方法,让编程人员更加方便高效的使用单元测试,从而提高软件的质量。
面向方面编程[3](AOP),作为面向对象编程的一种补充和完善。它利用切面封装日志处理,权限认证,事务处理等影响系统多个类的公共功能,这些功能为系统的主要业务提供服务。我们将系统的主要业务称为核心关注点(主关注点),提供服务的功能称作横切关注点。在程序运行的时候,AOP 可以使用横切技术捕获需要这些横切关注点提供服务的地方(称为连接点),在连接点处“注入”横切关注点的逻辑。AOP 使用横切技术实现了横切关注点与系统关注点的分离,不仅降低了系统耦合性,而且保证了横切关注点的复用。AOP 横切技术分为两种:动态横切与静态横切。使用动态横切技术,可以在连接点处织入横切逻辑,也可以改写连接点处方法的实现,设定方法的返回值。静态横切可以修改程序的静态结构,可以向类中添加新的方法或变量,声明一个类实现一个接口或继承一个类,可以将检查异常转换为未检查异常。作为 AOP 的实现框架 AspectJ 很好支持 AOP 的各种功能。
通过深入研究 AOP 技术与 Java 单元测试,发现 AspectJ 能很好的解决上述Java 单元测试问题。本文详述了具体的解决方法,并且基于 JUnit 框架结合AspectJ设计实现了 TestWAJ,支持本文方法在开发中的使用。
当前软件项目往往十分复杂,模块众多,整个系统的正常运行依赖于每个小功能模块的正常运行,单元测试是确保单一功能正常工作的有效手段,所以单元测试在项目开发中愈发不可或缺。随着测试驱动开发理念的流行,单元测试被越来越广泛的使用。但是,为系统中每个功能编写单元测试,还要确保单元测试的质量,要花费开发人员大量时间。对大项目而言,大量的单元测试导致巨大的测试的成本。复用是减少工作量的重要手段,因此有众多的关于单元测用例复用的研究。文献[4]通过分析用例的可复用属性,并定义与复用度有关的度量元,使得可以定量计算各测试用例的复用度,为复用提供依据。文献[5][6]通过修复由于方法演化而失效的测试用例,实现测试用例的复用。文献[7]使用 XML 描述测试用例,使测试用例的描述规范化,方便程序分析,同时还为测试用例建立复用文档,包括测试用例的类别信息,历史复用信息,方便测试用例的复用。有的研究者并不满足仅仅复用已有的测试用例而已,他们探索各种测试用例的自动生成技术。
文献[8]主要通过软件需求规格说明书,得到程序流程图,将流程图改造成更易于测试用例生成的形式流程图,通过形式流程图自动生成交互有限状态自动机,再由有限状态机生成测试路径,依据测试路径最终生成测试用例。文献[9]设计了一种从系统测试中录制出单元测试的方法。
为了弥补单元测试中模拟框架的不足。文献[10]扩展模拟框架的功能,让用户可以 Mock 被测对象自身的属性和方法。文献[11]使用 Hook 技术使得模拟框架可以解决没有对象概念的基于过程的函数模拟问题。
面向方面编程,作为面向对象技术的完善和补充,其强大的横切功能,可以帮助解决诸多面向对象中的难题。文献[12]将现有的软件监控模式用切面封装,要使用监控功能时,通过横切功能注入,实现了监控功能的复用,而且用法更灵活。文献[13]使用 AOP 的动态织入功能,实现监测器的动态增删,还可以实现对方法级别,WEB应用中 Action级别的监测。
也有人将面向方面应用于测试领域,并且取得了不小的成就。文献[14]主要使用 AOP 的横切功能获得测试运行时指定连接点处的信息,通过这些信息来检测单元测试的有效性。文献[15]利用 AOP 横切功能将各种类型的错误注入被测系统,来测试软件的安全性。文献[16],将面向方面应用于单元测试,利用 AOP 功能实现直接测试对象私有属性,为被测类提供模拟对象,实现日志功能,性能测试功能,并将这些功能集成在一个框架中。
前面的研究者试图通过测试用例复用,测试用例的自动生成来减轻开发人员进行单元测试的负担,节约测试成本。但由于测试用例种类繁多,前述复用手段不可能覆盖所有的测试用例,具有局限性。自动生成测试用例的方法要么过于复杂,要么依赖于其他条件。本文另辟蹊径,1.从运行测试用例入手,使用 AspectJ实现了方便地选择测试用例,自动地添加运行测试用例的功能,省去测试人员在运行测试时大量的手工操作。可为拥有众多测试用例的项目节约大量运行成本。
2.从复用测试方法的角度入手,使用 AspectJ,设计了参数化测试,接口测试方法复用两种复用测试方法的模式。本文的方法切切实实的减轻开发人员的负担。
使模拟框架可以解决对象自身方法和属性的模拟问题,虽然扩展了模拟框架的功能,但仍然没有解决模拟框架不能模拟 final 类,代理静态方法等缺陷(后文还将说明其它缺陷)。使用 Hook 技术只是解决面向过程中的模拟问题。本文将使用 AspectJ 轻松实现一种比现有框架功能更强并且克服现有框架缺陷的解决方案。
虽然已经有人将 AOP 运用于测试,但只是简单使用了 AOP 的某项功能,没深入挖掘 AOP 的潜力。文献[16]虽然用 AOP 实现 mock功能,但只提供了桩对象的功能,没有提供模拟对象可以验证交互的功能。本文在深入研究 AspectJ 与Java单元测试的基础上,综合运用静态横切,动态横切,切面继承等多种 AspectJ功能,解决了 Java单元测试中比较重要的几个问题,十分有意义。
运用TestWAJ进行单元测试系统:
TestWAJ 的包结构
基于状态的测试
测试类类图
自定义 TestSuite的时序图
解析测试类得到 TestSuite的时序图
addTestMethod方法代码
桩对象与被测类之间的交互
目 录
中文摘要
英文摘要
1 绪 论
1.1 研究背景及意义
1.2 国内外研究现状分析
1.3 论文的主要工作
1.4 论文的组织安排
2 面向方面编程(AOP)
2.1 面向方面编程概述
2.1.1 AOP的基本概念
2.1.2 AOP技术的优势
2.2 面向方面编程的实现
2.3 AspectJ
2.3.1 连接点
2.3.2 切点
2.3.3 通知
2.3.4 类型间声明
2.3.5 方面
2.4 面向方面的模型表示
2.5 本章小结
3 JAVA单元测试
3.1 Java单元测试框架
3.2 JUnit单元测试框架
3.3 单元测试中模拟对象的使用
3.3.1 模拟对象的意义
3.3.2 基于状态的测试与基于交互的测试
3.3.3 EasyMock简介
3.4 本章小结
4 AspectJ在单元测试中的应用研究
4.1 运用AspectJ组织运行单元测试
4.1.1 当前单元测试用例执行方式存在的问题分析
4.1.2 基于 AspectJ 的解决思路
4.2 运用AspectJ解决单元测试依赖问题
4.2.1 现有隔离框架存在的问题分析
4.2.2 使用 AspectJ 技术解决 Mock问题
4.3 基于AspectJ的单元测试复用性研究
4.3.1 参数化测试
4.3.2 接口测试方法的复用
4.4 本章小结
5 TestWAJ
5.1 TestWAJ简介
5.2 TestWAJ的组成
5.3 运用TestWAJ进行基于状态和基于交互的测试
5.4 运用TestWAJ进行参数化测试与接口测试方法复用
5.5 运用TestWAJ灵活高效选择运行测试
5.6 本章小结
6 总结与展望
6.1 总结
6.2 研究展望
致 谢
参考文献
附 录
(如您需要查看本篇毕业设计全文,请您联系客服索取)