spring中使用mockito

时间:2023-03-09 13:21:12
spring中使用mockito

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的时候,否则可能会影响其他人的测试。