转:http://blog.csdn.net/yys79/article/details/66472797

最近,项目中频繁用到dubbo,而且java工程用引用了几十个关联系统的服务(如用户认证,基础服务,客户平台)。这些服务都是dubbo服务,对我们仅提供了一个接口,服务通过zookeeper注册,并给我们提供服务。我们的项目都是基于spring的。spring集成dubbo,就可以对这些外部服务进行注入和使用了。

但是对于单元测试来说却出现了难题:领域模型的测试不是问题,主要都是自己的代码,加上一些mock就可以轻松测试;但是如果我想测试应用服务层(使用外部服务最多的地方),很多情况下就需要启动spring环境,而这样就需要加载外部系统的服务了。问题是外部的服务给我们的jar包中,只有服务的接口。启动时如果按照正常开发环境的配置加载spring context,那么明显是依赖了外部环境,如果没有启动zookeeper或者本机不联网,抑或是关联系统没有启动,spring context加载将会失败,这是单元测试的忌讳。如果使用专门的单元测试的spring配置文件,去掉外部关联系统的consumer配置,启动会直接失败,更别提测试了。

还有写其他问题,如测试静态方法,私有方法;mock框架与springtest如何集成。spring的aop代理类如何mock一些默认的实现,测试数据库如何选择。总之问题超多。好吧,该进入正题了。

1.测试静态类,私有方法的问题

简单一句话,用powermock。powermock可以做到修改字节码而改变类的行为,这不多说了,大家自己搜一下,官网上例子通俗易懂。目前我在maven中的关于powermock,mockito的依赖是这样加入的:

  1. <dependency>
  2. <groupId>org.powermock</groupId>
  3. <artifactId>powermock-api-mockito</artifactId>
  4. <version>1.6.6</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.mockito</groupId>
  8. <artifactId>mockito-all</artifactId>
  9. <version>1.10.19</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.powermock</groupId>
  13. <artifactId>powermock-module-junit4</artifactId>
  14. <version>1.6.6</version>
  15. <scope>test</scope>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.powermock</groupId>
  19. <artifactId>powermock-module-junit4-rule-agent</artifactId>
  20. <version>1.6.6</version>
  21. <scope>test</scope>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.powermock</groupId>
  25. <artifactId>powermock-module-junit4-rule</artifactId>
  26. <version>1.6.6</version>
  27. <scope>test</scope>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.jacoco</groupId>
  31. <artifactId>org.jacoco.agent</artifactId>
  32. <classifier>runtime</classifier>
  33. <version>0.7.9</version>
  34. <scope>test</scope>
  35. </dependency>

最后的话这个jacoco不是mock的依赖,是一个测试覆盖率的插件。也推荐一下给大家用,哈哈。

2.powermock与springtest配合使用的问题

第一个问题解决了,不错!第二个问题就来了。spring标准的Runner是SpringJUnit4ClassRunner,如果用这个Runner,那么powermock的@PrepairForTest就没法使用了(也就是静态mock,私有方法mock的关键),因此如果想使用静态和私有方法mock就必须使用用Powemock的Runner,但是又如何启动spring context呢?

经过一些查找,终于解决了这个问题,方法就是用powermock的代理, 在测试类上加上这样的注解:

  1. @PowerMockIgnore({"java.lang.management.*","javax.management.*","javax.xml.*","org.xml.sax.*","org.apache.xerces.*","org.w3c.*"})
  2. @RunWith(PowerMockRunner.class)
  3. @PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
  4. @ContextConfiguration(locations = "classpath:META-INF/spring/test-spring.xml")

Runner使用PowerMockRuner(就是RunWith注解的值);使用powermock提供的代理来使用SpringJUnit4ClassRunner;@PowerMockIgnore的作用是忽略一些powermock使用的classloader无法处理的类,不使用的话,启动用例就会报错。

  1. classpath:META-INF/spring/test-spring.xml 是单元测试专门的spring配置文件,和域代码使用的配置有些不同。这个文件我放在/test/resources/spring/目录下。

到此,一个基于PowerMock,springtest和Mockito的基本配置就都弄完了。

上一篇说到powermock的配置,我一般在测试类中再加上继承spring的测试类:extends AbstractTransactionalJUnit4SpringContextTests ,这样就基本可以了。

再来说说上一篇中使用的spring配置文件。主要的不同就是test-spring.xml里面不会包含哪些引用外部服务的consumer,也就是剔除外部dubbo服务。

但是代码里有很多注入外部服务的地方,这如何处理呢?这是第三个问题:

3.注入外部的服务:

开始我想了个很笨的方法:在test/文件夹下给外部服务的接口都提供一个空的实现类(implements 接口,然后用eclpse生成默认的方法实现)。这样基本上就可以启动了。但是实际使用中,由于外部服务接口也在不断修改中,会出现不同环境的接口类不一至的情况。比如uat环境的jar包多了或一个方法(虽然我们的程序没有直接使用),如此一来,我自己搞的空实现类就会报编译错误了。

后来想到了一个方法,在/test的代码中增加一个普通的@Conponent注解的类,类里面使用@Bean注解标明所有外部类的生成方法

  1. @Component
  2. public class MockedOuterBeanFactory {
  3. @Bean
  4. public OuterService outerSerive(){
  5. return Mocktio.mock(OuterService.class);
  6. }
  7. }

然后在测试类中注入这个MockedOuterBeanFactory,这样测试环境的spring就可以完整的启动了。外部的服务在启动后都是Mocktio生成的代理类,所有方法都会返回默认值。

在实际测试中如何打桩呢?也很简单。

如果我测试一个自己写的服务(如MyService),MyService又注入了OuterService(外部服务),那么利用spring Bean注入的单例这个特性就可以完成。在MyService的测试类中(MyServiceTest.java),同样也注入OuterService,在执行MyService的方法之前对OuterService进行打桩。那么由于bean是单例的,MyServiceTest中注入的OuterService实例就是MyService注入的实例。这样就轻松完成了打桩的工作。如果有特殊原因,main中配置的bean不是单例的,那么可以的话,在test-spring.xml中把它配置为单例的就可以。如果确实情况特殊不允许配置为单例方式,看下一篇吧。

启动后

解决了spring启动的问题,然后呢?数据库

4.测试数据库的选择

有时候,我们需要测试持久化的内容,比如分页查询,不能说测试覆盖了代码就可以,还需要验证查询到的数据是否符合要求。参考了dbunits之类的东西,最后还是觉得之前使用的h2database是最好的选择。它可以使用内存模式,不需要外部数据库的依赖。这样单元测试才能独立运行。配置很简单,

首先加入依赖:

  1. <dependency>
  2. <groupId>com.h2database</groupId>
  3. <artifactId>h2</artifactId>
  1. <span style="white-space:pre">          </span><version>1.4.191</version>
  2. </dependency>

至于版本,就自己找个最新的吧。

然后在数据源的地方使用如下配置(这也是测试环境spring配置不同于main配置的主要位置):

  1. <bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource"
  2. destroy-method="close">
  3. <property name="poolProperties">
  4. <bean class="org.apache.tomcat.jdbc.pool.PoolProperties">
  5. <property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=1;MODE=MySQL"/>
  6. <property name="driverClassName" value="org.h2.Driver" />
  7. <property name="username" value="" />
  8. <property name="password" value=""/>
  9. <property name="validationQuery" value="SELECT 1"/>
  10. <property name="maxActive" value="8" />
  11. <property name="minIdle" value="1"/>
  12. <property name="maxIdle" value="4" />
  13. <property name="maxWait" value="10000"/>
  14. <property name="initialSize" value="1"/>
  15. </bean>
  16. </property>
  17. </bean>

注意:MODE=MySQL,这是让h2模拟mysql库,如果你使用其他类型的库,一般也会有对应的Mode,主流数据库都支持。注意mem项,意思是内存数据库,这样配置根本不会生成数据库文件的,特别适合单元测试(依赖外部环境就不是标准单元测试了)。至于数据源类型,按自己的工程的配置就好,只要使用h2的url和driver就行,这里用的是tomcat数据源。

这些配置都做好后,就可以运行真正的powermock,mockito,springtest的单元测试了。下一篇说说怎么测试aop的类。

上两篇中,基本环境和测试方式都说了一下。基本的测试否没问题了。但是还有些问题需要解决。在我实际的开发中,最主要是是要做有Aop切面的Bean内部注入的bean打桩。

基本情况是:

MyService是个接口,其实现类MyServiceImpl是@Transactional注解的Bean(这样注入的MyService实例实际上就是代理了)

MyServiceImpl注了一个Bean:InnerBean,innerBean是自己工程中实现或其他服务都无所谓

测试中想使用mock替换这个InnerBean。

在spring中,aop用代理实现的。PowerMock不能修改其字节码。而在测试中,我需要替换MyService代理中的InnerBean实例。开始伤透了脑筋啊。。。

如果不能打桩,那么必须老老实实的准备fixture才能测试,比如准备数据库中多个表的数据,才能保证InnerBean完成我的预期结果(这种情况还算好的,有些情况都不能打桩)。

这个其实真是不难,只不过之前不太熟悉spring的测试框架(以前抛弃了spring,所以也不怎么研究)。

springtest有2个Utils类,可以帮助我们拿到MyService代理中的具体实现类:

  1. org.springframework.test.util.AopTestUtils;
  2. org.springframework.test.util.ReflectionTestUtils;
  1. MyServiceImpl impl = org.springframework.test.util.AopTestUtils.getTargetObject(MyServiceBean实例);

这样就可以拿到具体实现类了,再加一句impl.innerBean = mockInnerBean;就可以用自己打桩过的mock替换注入的innerBean实例了。如果多于一个测试方法,别忘了finally时候替换回来啊。

impl.innerBean 这里,我一般的注入bean都是是用package级别的,这样便于测试,不必特别的依赖其他技术就可以替换实现。如果是private的,那么用ReflectionTestUtils吧,具体不用说了,简单易用。

dubbo应用程序的单元测试环境搭建(springtest,powermock,mockito)的更多相关文章

  1. Dubbo使用详解及环境搭建

    一:Dubbo简介 Dubbo是阿里巴巴提供的开源的SOA(面向服务的体系结构)服务化治理的技术框架,据说只是一部分开源的,但一些基本的需求已经可以满足的,而且可扩展性.是一种能取代PHRPC的服务调 ...

  2. 微信小程序的开发环境搭建(Windows版本)

    前言: 小程序是指微信公众平台小程序,小程序可以帮助开发者快速的开发小程序,小程序可以在微信内被便捷地获取和传播:是一种不需要下载安装即可使用的应用小程序,和原有的三种公众号是并行的体系.2017年1 ...

  3. [微信小程序] 认识微信小程序及开发环境搭建

    微信公众平台首页 https://mp.weixin.qq.com 微信公众平台测试帐号系统 https://open.weixin.qq.com/connect/qrconnect?appid=wx ...

  4. 使用jasmine-node 进行NodeJs单元测试 环境搭建

    关于jasmine就不多说了,关于语法请参加官方文档.http://pivotal.github.io/jasmine/ 关于NodeJS的单元测试框架有多种,如果要在NodeJS中使用jasmine ...

  5. Karma和Jasmine 自动化单元测试环境搭建

    最近初学AngularJS ,看到的一些教程中经常有人推荐使用Karma+Jasmine来进行单元测试.自己之前也对Jasmine有些了解,jasmine也是一个不错的测试框架. 1. karma介绍 ...

  6. 基于Netbeans的PHPUnit单元测试环境搭建

    一.配置 PHPUnit截至2015-10-16,稳定版已更新至5.0.6,要求使用PHP v5.6及以上的环境才能使用. PHPUnit的4.8系列要求在PHP v5.3.3以上环境使用. Netb ...

  7. 微信小程序初窥-环境搭建

    关于微信小程序的背景知识,在此不做阐述,可以自行搜索了解.本文将介绍微信小程序的账号的注册,IDE的下载,创建一个实例小程序. 1.注册小程序 前去链接:https://mp.weixin.qq.co ...

  8. 前端单元测试环境搭建 Karma Jasmine

    Karma 官网On the AngularJS team, we rely on testing and we always seek better tools to make our life e ...

  9. 用maven搭建 testNG+PowerMock+Mockito测试框架

    单元测试是开发中必不可少的一部分,是产品代码的重要保证. Junit和testNG是当前最流行的测试框架,Junit是使用最广泛的测试框架,有兴趣的话自己baidu一下. testNG基于Junit和 ...

随机推荐

  1. Android App程序结构

    先看结构图: ====================================== 1.   /src   源码目录,不解释. 2.  /gen   gen目录是ADT 自动生成的代码所在位置 ...

  2. nmon系统监控

    nmon系统监控篇 本文目录 1 nmon的安装 2 nmon控制台指令 3 输出监控文件 一 下载nmon 本次使用的是nmon_linux_14i.tar.gz 二 放入linux中后解压 gzi ...

  3. 使用原app接口进行微信公众号开发

    1.跨域问题 原来的app项目已经上线,然而接下来就有意思了,突然上头说要把app的发件功能复制到微信公众号里.那么问题来了,微信公众号的页面是前端和交互式h5大哥写的. 那么就将页面丢微信里,请求我 ...

  4. ValueStack、ActionContext

    笔者不知道该用哪个词来形容ValueStack.ActionContext等可以在Struts2中用来存放数据的类.这些类使用的范围不同,得到的方法也不同,下面就来一一介绍. 1. ValueStac ...

  5. Arcengine 基本操作(待更新)

    /// <summary> /// 删除fieldName属性值为1的弧段 /// </summary> /// <param name="fieldName& ...

  6. 论文笔记《Notes on convolutional neural networks》

    这是个06年的老文章了,但是很多地方还是值得看一看的. 一.概要 主要讲了CNN的Feedforward Pass和 Backpropagation Pass,关键是卷积层和polling层的BP推导 ...

  7. Chrome 浏览器访问 Google 学术出现问题 “but your computer or network may be sending automated queries. ”

    问题: Chrome 浏览器访问 Google 学术出现如下的问题 : ... but your computer or network may be sending automated querie ...

  8. python 小练习1

    _input = ['I',6,6,'love','python',6] _str = '' _sum = 0 for item in _input: if isinstance(item,str): ...

  9. 【08】node 之 fs文件

    var fs = require("fs");//fs 系统文件模块,对文件进行操作.Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有 ...

  10. Fence(codeforces 232D)

    题意: 对于给定的a[1..n],定义区间[s,t]和[x,y]"匹配"当且仅当下列条件同时满足:1. t-s=y-x,即长度相同.3. t<x或s>y,即两区间没有交 ...