1 mockito介绍和入门

官方:https://github.com/mockito/mockito

入门:

5分钟了解Mockito http://liuzhijun.iteye.com/blog/1512780

Mockito:一个强大的用于 Java 开发的模拟测试框架 http://www.oschina.net/translate/mockito-a-great-mock-framework-for-java-development

2 spring中正常使用mockito

上demo代码:

 @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:testApplicationContext.xml" })
public class SpringMockitoTest {
 
    @Mock
    private ApiService mockApiService;
 
    @Before
    public void initMocks() {
        MockitoAnnotations.initMocks(this);
        when(mockApiService.test()).thenReturn("ok");
    }
 
    @Test
    public void should_success_when_testApiService() {
        String result = mockApiService.test();
        Assert.assertEquals("ok", result);
    }
}
 
@Component
public class ApiService {
 
    @Autowired
    private TestApiService testApiService;
 
    public String test() {
        String connect = testApiService.connect();
        connect += "test";//test自己的业务
        return connect;
    }
}
 
@Component
public class TestApiService {
    public String connect() {
        return "error";
    }
 
    public String  findFromDb() {
        return "db_data";
    }
}

正常使用spring和mockito中,我们把需要的mock的ApiService给mock掉,但是我们更想的是把TestApiService中的connect方法mock掉,这样就可以测试我们自己的代码,也就是ApiService中test方法自己的业务。

3 spring中mock任何容器内对象

上面的demo中,我们如何mock掉TestApiService中的test方法?

因为TestApiService是spring容器管理的bean,并且ApiService中使用到TestApiService,所以我们把ApiService中引用的TestApiService替换成我们的mock对象即可。

Spring框架有个反射工具ReflectionTestUtils,可以把一个对象中属性设置为新值,我们可以使用:

ReflectionTestUtils.setField(apiService, "testApiService", spyTestApiService);

把我们mock的testApiService放到apiService中,这样apiService调用就是我们mock的对象了;但是默认spring中apiService对象是代理对象,不能直接把值设置到属性上,所以我们自己写个小的工具类,在最后如下:

ReflectionTestUtils.setField(AopTargetUtils.getTarget(apiService), "testApiService", spyTestApiService);

完整demo:

 @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:testApplicationContext.xml" })
public class SpringMockitoTest {
 
    @Autowired
    private ApiService apiService;
    @Mock
    private TestApiService spyTestApiService;
    @Autowired
    private TestApiService testApiService;
 
    @Before
    public void initMocks() throws Exception {
        MockitoAnnotations.initMocks(this);
        ReflectionTestUtils.setField(AopTargetUtils.getTarget(apiService), "testApiService", spyTestApiService);
        when(spyTestApiService.connect()).thenReturn("ok");
    }
 
    @After
    public void clearMocks() throws Exception {
        ReflectionTestUtils.setField(AopTargetUtils.getTarget(apiService), "testApiService", testApiService);
    }
 
    @Test
    public void should_success_when_testApiService() {
        String result = apiService.test();
        Assert.assertEquals("oktest", result);
    }
}
 
@Component
public class ApiService {
 
    @Autowired
    private TestApiService testApiService;
 
    public String test() {
        String connect = testApiService.connect();
        connect += "test";//test自己的业务
        return connect;
    }
}
 
@Component
public class TestApiService {
    public String connect() {
        return "error";
    }
 
    public String  findFromDb() {
        return "db_data";
    }
}
 
public class AopTargetUtils {
    /**
     * 获取 目标对象
     * @param proxy 代理对象
     * @return 
     * @throws Exception
     */
    public static Object getTarget(Object proxy) throws Exception { 
        if(!AopUtils.isAopProxy(proxy)) { 
            return proxy;//不是代理对象 
        } 
        if(AopUtils.isJdkDynamicProxy(proxy)) { 
            return getJdkDynamicProxyTargetObject(proxy); 
        } else { //cglib 
            return getCglibProxyTargetObject(proxy); 
        } 
    } 
 
    private static Object getCglibProxyTargetObject(Object proxy) throws Exception { 
        Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); 
        h.setAccessible(true); 
        Object dynamicAdvisedInterceptor = h.get(proxy); 
        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); 
        advised.setAccessible(true); 
        Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget(); 
        return target; 
    } 
 
 
    private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { 
        Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); 
        h.setAccessible(true); 
        AopProxy aopProxy = (AopProxy) h.get(proxy); 
        Field advised = aopProxy.getClass().getDeclaredField("advised"); 
        advised.setAccessible(true); 
        Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget(); 
        return target; 
    } 
}

最后就是注意测试之后要还原现场,把spring对象还原,尤其在跑maven test的时候,否则可能会影响其他人的测试。

spring中使用mockito的更多相关文章

  1. Spring Boot项目中使用Mockito

    本文首发于个人网站:Spring Boot项目中使用Mockito Spring Boot可以和大部分流行的测试框架协同工作:通过Spring JUnit创建单元测试:生成测试数据初始化数据库用于测试 ...

  2. Velocity初探小结--Velocity在spring中的配置和使用

    最近正在做的项目前端使用了Velocity进行View层的数据渲染,之前没有接触过,草草过了一遍,就上手开始写,现在又回头细致的看了一遍,做个笔记. velocity是一种基于java的模板引擎技术, ...

  3. Spring中Bean的作用域、生命周期

                                   Bean的作用域.生命周期 Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).protot ...

  4. Spring中Bean的实例化

                                    Spring中Bean的实例化 在介绍Bean的三种实例化的方式之前,我们首先需要介绍一下什么是Bean,以及Bean的配置方式. 如果 ...

  5. 模拟实现Spring中的注解装配

    本文原创,地址为http://www.cnblogs.com/fengzheng/p/5037359.html 在Spring中,XML文件中的bean配置是实现Spring IOC的核心配置文件,在 ...

  6. Spring中常见的bean创建异常

    Spring中常见的bean创建异常 1. 概述     本次我们将讨论在spring中BeanFactory创建bean实例时经常遇到的异常 org.springframework.beans.fa ...

  7. Spring中配置数据源的4种形式

    不管采用何种持久化技术,都需要定义数据源.Spring中提供了4种不同形式的数据源配置方式: spring自带的数据源(DriverManagerDataSource),DBCP数据源,C3P0数据源 ...

  8. spring中InitializingBean接口使用理解

    InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法. 测试程序如下: imp ...

  9. Quartz 在 Spring 中如何动态配置时间--转

    原文地址:http://www.iteye.com/topic/399980 在项目中有一个需求,需要灵活配置调度任务时间,并能自由启动或停止调度. 有关调度的实现我就第一就想到了Quartz这个开源 ...

随机推荐

  1. SQlserver 行转列

    列转行编程中很容易碰到,小弟在此总结下, 行转列暂时还没遇到,遇到再补充. 列转行:  , , , ,  以上都是以逗号分隔,分隔符可以自定义.

  2. 复利test

    因为上节课老师有给我们讲了单元测试的概念及其基本操作,我就对我之前的复利计算器进行了改进和测试.因为从控制台的简单输入输出对单元测试来说不够完善,便参考了其他同学的优秀编程又重新写了一个复利计算器的框 ...

  3. eclipse启动无响应,停留在Loading workbench状态,或老是加载不了revert resources

    做开发的同学们或多或少的都会遇到eclipse启动到一定程度时,就进入灰色无响应状态再也不动了.启动画面始终停留在Loading workbench状态.反复重启,状态依旧. 多数情况下,应该是非正常 ...

  4. firame标签: IHTMLElement -> IHTMLFrameBase2 -> IHTMLWindow2 -> IHTMLDocument2 跨域访问

    获得iframe标签的元素指针 CComPtr<IHTMLElement> spAdIframe = ... CComQIPtr<IHTMLFrameBase2> spFram ...

  5. C++Builder 中 Enter键或者Tab键无效

    VC++中或者C++Builder中 当回车键不能换行时应该就是因为自己不小心按到了键盘上的insert键,要想恢复只需要再按一下就可以了. 小经验

  6. 【10_169】Majority Element

    今天遇到的题都挺难的,不容易有会做的. 下面是代码,等明天看看Discuss里面有没有简单的方法~ Majority Element My Submissions Question Total Acc ...

  7. Java 第一课

    怎么去学习java: 1,课前预习,加深对课上学习的吸收! 2,课后预习,让我们能够对所学内容的掌握! 3.通过画流程图,锻炼逻辑思维能力,学会从程序员的角度去看到问题,摒弃以前的惯性的思维方式!

  8. [转]Flash Socket通信的安全策略

    昨天做测试的时候遇到一个问题,做好的SWF在Flash AS3中调试通过,但是发布到html中之后就无法得到数据了.查了一些资料之后找到了解决办法.这里感谢 剑心 提供帮助,以及同事若水三千提供Jav ...

  9. Eclipse的常用快捷键、旁门左道、系统错误小贴士

    转自:http://usenrong.iteye.com/blog/1113700 eclipse是一个开源产品,目前比较流行和普遍的JAVA IDE(集成开发环境)它使用工程化方式管理.虽然ecli ...

  10. 《The Book of CSS3》学习笔记

    一.浏览器前缀 E{ -moz-name : value; /* Firefox */ -ms-name : value; /* IE */ -o-name : value; /* Opera */ ...