ImageVerifierCode 换一换
格式:DOCX , 页数:15 ,大小:201.57KB ,
资源ID:6449531      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/6449531.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(一个通用的单元测试框架的思考和设计.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

一个通用的单元测试框架的思考和设计.docx

1、一个通用的单元测试框架的思考和设计一个通用的单元测试框架的思考和设计01-思考篇发布时间: 2011-8-02 13:08 作者: CrazyCoder2010 来源: 51Testing软件测试网采编 字体: 小 中 大 | 上一篇 下一篇 | 打印 | 我要投稿 | 推荐标签: 软件测试 单元测试1. 先从问题说起写过程序的同学都知道,做好单元测试提高代码覆盖率对整个项目意味着什么,但是做好单元测试并不是一件那么简单的事情,因为实际业务逻辑和运行环境的复杂性,导致了我们的单元测试代码不可能都像那些helloWord那么简单,比如现在的业务系统绝大多数都是基于数据库的,怎么做单元测试才能做到

2、每次做单元测试时都是一个干净的测试环境-即上次单元测试的数据库操作不会影响本次测试的结果(比如一个createUser操作,第一次单元测试运行成功了,但是第二次运行却失败了,因为代码里做了重名判断),还有web层的单元测试如何进行,web框架如何启动,那些万恶的httpServletRequest,HttpSession等接口,头大。2. 现有的框架及对比目前有很多的开源测试框架,一下列举几个最常用的junit这个地球人都知道,单元测试的先祖框架,核心,掌握他并熟练运用那是必须滴testNG,号称下一代单元测试框架,火了一阵子,功能比junit更强大,但是依旧无法撼动junit的王者地位dbu

3、nit,基于数据库的测试框架,提供了可以通过xml格式来准备数据的方案,使测试数据和真实的数据库数据分离开,测试者可以针对自己在文件中造的数据进行程序的断言,也提供了一个与junit集成的基类DBTest,这个基类封装了一些dbunit启动的操作,把一些通用复杂的操作对操作者透明,但是这也是一个劣势,就是一旦继承了这个基类,你就无法继承自己的类(都是java单继承惹的祸)SpringTest框架,spring2。5以后提供了一个test框架,提供了大量的基类,测试框架会自动启动spring容器,把spring中的bean自动注入进来,另外还提供了可以对测试方法进行事务控制,每个测试方法测试完成

4、后通过框架控制自动回滚的功能,优势很明显,充分发挥了spring框架的优势,对开发者屏蔽细节,自动事务控制,缺点也很明显,测试必须依赖于spring框架,对于那些没用到spring框架的项目来说只能望洋兴叹了Struts2Test,struts2提供的单元测试用例基类,提供对httprequest,response,session等对象mock(基于springtest框架),可以初始化struts框架,这对基于struts2的应用系统来说是个福音,不幸的是要用这个框架,必须依赖spring框架,否则一切玩完3. 框架设计的目标核心的框架都在上面了,不难看出,在这几个主流框架的使用中,spri

5、ng框架在其中扮演了很重要的角色,如果项目中没有用到spring,有些测试框架的支持功能根本无法使用!如何让各个框架可以很容易的整合起来又能发挥自身最大的优势呢?在进行具体的设计之前,我们来展望一下我们期望的测试流程是怎么样的(图)测试开始前记录数据库状态是1,开始测试后把属于某个测试用例的准备数据加载到数据库中,这时候数据库的状态已经变成了2,接着运行具体的测试用例,测试方法中有对数据库的操作,因此变成了状态3,我们希望对测试的方法进行事务控制,这样在该方法内做的所有数据库操作都可以在测试完成后被会被回滚到先前状态,因此测试方法执行完后,状态又变回了2,当该测试用例的测试方法都执行完毕后,框

6、架负责把先前插入的准备数据删除,这样数据库状态又回到了原始的状态1,这样的优势非常明显,无论这个测试用例执行多少次,用例执行完毕后测试后都会回到原始状态,不会对现有数据造成污染又能保证每次执行都用相同的数据,何乐不为?刚刚谈了理想,接下来看一下怎样的一个框架才能满足我们的需求1)不需要和任何框架耦合在一起这点很重要,整个框架应该围绕一个基本测试框架展看如testng或junit而不是spring这种重量级的框架(相对junit来说spring已经是很重量级的东东了)2)可以很容易为框架添加新的特性,但是不需要改动框架本身的代码-对于springtest或dbunit等框架,相对于我们的测试框架

7、只是一些功能上的enhancement,可以通过功能扩展加到框架中,而不是通过继承他们提供基类的方式来直接依赖他们3)准备测试数据做基于db的单元测试的人都有感触,单元测试很大一部分时间都在准备数据,因此框架应该支持让使用者通过更加便利的方式去准备数据如常用的excel文件或xml文件或其他csv格式框架使用者不需要去手动的加载这些数据准备文件,框架需要提供一种契约,如文件名和类名名称路径保持一致,框架自动为当前测试类加载对应的数据,框架使用者只需要书写测试代码,把测试数据放到指定文件夹下即可4)事务控制这个没什么捷径,只能靠jdbc的api来实现。一个通用的单元测试框架的思考和设计02-设计

8、篇发布时间: 2011-8-03 10:59 作者: CrazyCoder2010 来源: 51Testing软件测试网采编 字体: 小 中 大 | 上一篇 下一篇 | 打印 | 我要投稿 | 推荐标签: 软件测试 单元测试第一节里介绍了我们框架设计的目标,这篇主要介绍的是这个框架主要的设计思路和关键技术点1. 如何扩展junit的功能,使junit在启动时可以做一些我们定制化的功能?junit4建立了以Runner为核心的测试框架运行机制,在junit3的版本中,我们知道要运行一个junit测试用例,必须继承一个TestCase基类,junit4则不需要这个限制,只需要标注一下要运行测试的方

9、法为Test就可以了,怎么做到的呢?就是这个Runner机制,这里不介绍Junit4的运行机制,可以从org。junit。runner。BlockJUnit4ClassRunner中得到答案,像springtest框架也是扩展了这个类的功能来达到扩展目的的2. 如何让junit4框架提供更多的自定义注解的功能?junit4提供了诸如Test,BeforeClass,Before等注解来简化单元测试过程,在我们这个通用框架的设计中,我们系统提供更多自己定制的注解来扩展junit的功能,如我们希望提供一个DataSet注解,当测试类有此注解时,框架自动解释这个注解,并把当前类同级目录下加载与类名相

10、同的xsl文件,该文件里存放的是该单元测试类的准备数据,这样就能解决上一节中提到的测试文件和测试类之间约束的目的,开发只需要关注准备数据和写测试类,其他的事情都交给框架去搞吧,通过跟踪Junit的源码我们不难看出单元测试类的执行要经过ObjectcreateTest()方法(测试框架加载测试类)和StatementmethodInvoker(FrameworkMethodmethod,Objecttest)(执行测试方法),这样我们就可以通过扩展BlockJUnit4ClassRunner类通过覆写这两个方法,让其支持我们更多的功能特性3. 回顾一个测试用例的测试过程一个测试用例的执行大约可以

11、包含这样几个步骤,在创建测试实例后,测试方法前,测试方法执行后,测试方法抛出异常后,因此我们可以根据这些功能定义一个统一接口IUnitTestExecuteListener,接口里定义这4个方法prepareTestInstance-创建测试实例后beforeTest-测试方法前afterTest-测试方法执行后afterThrowable-测试方法抛出异常后这样对于不同的功能扩展,我们只需要提供相应的子类即可,如我们前面提到的那个DataSet注解的方式来加载测试数据准备文件,就可以提供一个ExcelDataProviderListener类,只要在其prepareTestInstance方

12、法里把测试文件内容读出来,插入到数据库中即可-soeasy!,对于要进行事务控制的测试方法Transactional标签,我们也可以提供一个实现类来实现事务控制的目的-这样对于框架而言,新功能的扩展只需要添加对应的子类即可,体现了软件设计的开-闭原则4. 框架执行流程图解释:IUnit是我们为这个通用框架YY的名字:)从这张流程图上可以看出,IunitRunner和IUnitTestExecutionListener是我们整个框架的核心,一个Runner有多个Listener,当测试的生命周期开始后,runner会循环调用已注册listener的prepareTestInstance,befo

13、reTest,afterTest,afterThrowable方法执行对应的功能5. 详细设计-类图解释:整个框架最核心的类和接口只有三个IunitRunner,这个是运行的切入点,用来注册每个测试类指定的listener(通过寻找测试中上的IUnitTestExecuteListeners标注),所有的功能扩展都是围绕IUnitTestExecuteListener展开,如类图中描述的GuiceStrapupListener用来启动guice容器,DataProviderListener用来加载测试准备数据6. 千呼万唤始出来-最终的测试用例长什么样子对于最终使用框架的开发者而言,根据自身需

14、要通过注解来动态加载所需要的listener即可(可以指定多个),注意测试用例上要加个RunWith标注,指定要执行的runner为IunitRunner这样junit框架才能用我们提供的runner来运行,实际使用的时候这些东东可以都放到一个测试父类中去完成,开发者只需要关注自身用到的listener即可viewplaincopytoclipboardprint?packagecom.crazycoder2010.iunit;importstaticorg.hamcrest.MatcherAssert.assertThat;importstaticorg.hamcrest.Matchers.

15、equalTo;importorg.junit.Test;importorg.junit.runner.RunWith;importcom.crazycoder2010.iunit.annotation.IUnitDataSet;importcom.crazycoder2010.iunit.annotation.IUnitTestExecuteListeners;IUnitDataSet(dbunitFile=AppTest.xml)RunWith(IUnitRunner.class)IUnitTestExecuteListeners(TransactionalListener.class,D

16、atasetProviderListener.class)publicclassAppTestextendsAbstractIUnitTestCaseTestpublicvoidtestHello()assertThat(hello,equalTo(hello);一个通用的单元测试框架的思考和设计03-实现篇-核心类源码发布时间: 2011-8-04 10:37 作者: CrazyCoder2010 来源: 51Testing软件测试网采编 字体: 小 中 大 | 上一篇 下一篇 | 打印 | 我要投稿 | 推荐标签: 软件测试 单元测试第二节里我们介绍了iunit整体的设计思路以及核心类之间

17、的关系,这篇将以源码+解释的方式来演示核心类的实现方式1. IUnitRunner类这个类是测试的入口类,直接继承自junit4。8的BlockJunit4ClassRunner,在构造函数里,我们把iunit框架的扩展功能添加了进来,因为整个框架呃设计都是基于Listener的,所以只需要把监听器在框架运行的时候加载进来即可-见构造函数,listener的注册是通过注解来进行的,因为测试类本身可能会有继承关系,因此需要遍历父类中的Listener,把子类+所有父类中的Listener合并起来,当然还要注意剔除掉重复注册的Listener,否则很可能导致一个Listener被执行多次(既在子类

18、中注册过了又在父类中注册过了)package com.crazycoder2010.iunit;import java.util.ArrayList;import java.util.List;import org.junit.runners.BlockJUnit4ClassRunner;import org.junit.runners.model.FrameworkMethod;import org.junit.runners.model.InitializationError;import org.junit.runners.model.Statement;import com.crazy

19、coder2010.iunit.annotation.IUnitTestExecuteListeners;public class IUnitRunner extends BlockJUnit4ClassRunner /* 监听器*/private List executeListeners = new ArrayList();private Class clazz;private TestContext testContext;public IUnitRunner(Class klass) throws InitializationError super(klass);/这个构造函数是jun

20、t的调用入口,这里我们把扩展功能的初始化写到其后this.clazz = klass;this.testContext = new TestContext();initListeners();private void initListeners()this.executeListeners.addAll(findListeners();/* 解析为当前测试类注册的监听器* return*/SuppressWarnings(“rawtypes”)private List findListeners()List result = new ArrayList();List listeners = n

21、ew ArrayList();Class c = this.clazz;while(c != null)IUnitTestExecuteListeners listener = c.getAnnotation(IUnitTestExecuteListeners.class);if(listener != null)for(Class l : listener.value()if(!listeners.contains(l)/去重listeners.add(l);c = c.getSuperclass();/查找父类中的监听器for(Class clazz:listeners)try resul

22、t.add(IUnitTestExecuteListener) clazz.newInstance();/通过反射将Listener实例加入到监听列表中 catch (InstantiationException e) e.printStackTrace(); catch (IllegalAccessException e) e.printStackTrace();return result;Overrideprotected Object createTest() throws Exception Object testInstance = super.createTest();/加上我们框

23、架的扩展功能this.testContext.setTestInstance(testInstance);for(IUnitTestExecuteListener executeListener : this.executeListeners)executeListener.prepareTestInstance(testContext);return testInstance;Overrideprotected Statement methodInvoker(FrameworkMethod method, Object test) for(IUnitTestExecuteListener e

24、xecuteListener : this.executeListeners)try executeListener.beforeTest(testContext); catch (Exception e) e.printStackTrace();Statement statement = null;for(IUnitTestExecuteListener executeListener : this.executeListeners)try statement = super.methodInvoker(method, test);executeListener.afterTest(test

25、Context); catch (Exception e) testContext.setThrowable(e);try executeListener.afterThrowable(testContext); catch (Exception e1) e1.printStackTrace();return statement;2、IUnitTestExecuteListener接口这个接口定义了测试用例执行生命周期的几个关键点packagecom.crazycoder2010.iunit.annotation;importjava.lang.annotation.Documented;im

26、portjava.lang.annotation.ElementType;importjava.lang.annotation.Inherited;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;importcom.crazycoder2010.iunit.IUnitTestExecuteListener;/*为TestCase注册监听器*authorKevin*/Retention(RetentionPolicy.

27、RUNTIME)Target(ElementType.TYPE)InheritedDocumentedpublicinterfaceIUnitTestExecuteListeners/*实际用到的监听器类*return*/Classvalue()default;3. AbstractIUnitTestCase测试基类这个类是为了便于测试定义了一个TestCase的基类,可以在此注册一些通用的监听器,注意Runwith(IUnitRunner.class)这个是关键,否则我们写在runner中扩展的功能是不会被junit4执行到的package com.crazycoder2010.iunit;

28、import org.junit.runner.RunWith;import com.crazycoder2010.iunit.annotation.IUnitTestExecuteListeners;RunWith(IUnitRunner.class)IUnitTestExecuteListeners(DatasetProviderListener.class)public class AbstractIUnitTestCase 一个通用的单元测试框架的思考和设计04-实现篇-测试数据自动插入删除发布时间: 2011-8-05 10:50 作者: CrazyCoder2010 来源: 51T

29、esting软件测试网采编 字体: 小 中 大 | 上一篇 下一篇 | 打印 | 我要投稿 | 推荐标签: 软件测试 单元测试上篇文章罗列了整个框架的核心接口,这篇文章将提供IunitExecutionListener的一个实现类-DatasetProviderListener,主要用来将测试数据插入到数据库中,待测试完成后自动删除数据1. dbunit为核心的db测试dbunit是个很好的数据库测试框架,提供了多种准备数据的操作策略来简化测试数据的插入或更新操作,参考http:/www.dbunit.org/components.html#deleteall操作 描述 DatabaseOperation.UPDATE 使用准备数据更新现有db中的数据(根据准备数据主键ID值),如果数据库中不存在准备数据中的ID,报错退出 DatabaseOperation.INSERT 将测试数据插入到数据库中,如果数据库中已经有对应id的数据,则报错退

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1