PowerMockito的基本使用解析
PowerMockito经常会结合Mockito使用,先说一下这2个的介绍:
1.Mockito和PowerMockito的简介
Mockito和PowerMockito是什么东西呢?他们有什么作用呢?
Mocktio和PowerMockito都是Mock的工具类,主要是Java的类库,Mock就是伪装的意思。
他们适用于单元测试中,对于单元测试来说,我们不希望依赖于第三方的组件,比如数据库、Webservice等。在写单元测试的时候,我们如果遇到了这些需要依赖第三方的情况,我们可以使用Mock的技术,伪造出来我们自己想要的结果。
对于Java而言,mock的对象主要是Java 方法和 Java类。
下面我就介绍一下怎么使用Mockito和PowerMockito去进行Mock。
2.Mockito和PowerMockito的区别
在我看来,PowerMockito是Mockito的一种增强,他们的PowerMockito可以调用Mockito的方法,但是对于Mocktio不能Mock的对象或者方法,我们可以使用PowerMockito来实现。
比如Mockito不能用于static Method, final method, 枚举类, private method,这些我们都可以用PowerMockito来实现,当PowerMockito和mockito结合使用的时候,我们需要考虑兼容性的问题。
两者的版本需要兼容
Mockito | PowerMockito |
---|---|
2.8.9+ | 2.x |
2.8.0-2.8.9 | 1.7.x |
2.7.5 | 1.7.0RC4 |
2.4.0 | 1.7.0RC2 |
2.0.0-beta - 2.0.42-beta | 1.6.5-1.7.0RC |
1.10.8 - 1.10.x | 1.6.2 - 2.0 |
1.9.5-rc1 - 1.9.5 | 1.5.0 - 1.5.6 |
1.9.0-rc1 & 1.9.0 | 1.4.10 - 1.4.12 |
1.8.5 | 1.3.9 - 1.4.9 |
1.8.4 | 1.3.7 & 1.3.8 |
1.8.3 | 1.3.6 |
1.8.1 & 1.8.2 | 1.3.5 |
1.8 | 1.3 |
1.7 | 1.2.5 |
Ref:https://github.com/powermock/powermock/wiki/Mockito
3.具体用法
本文实现实现需要构造的接口和需要返回值的接口
引入依赖
<dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>2.0.2-beta</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>1.7.4</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>1.7.4</version> <scope>test</scope> </dependency> </dependencies>
需要Mock的类:
ProcessDB.java
package com.github.mock.simple.vo; public class ProcessDB { public ProcessDB(String ss){ System.out.println(ss + " Enter ProcessDB ..."); } public ProcessDB(){ System.out.println("Enter ProcessDB ..."); } public void getResultOfConnectDBNoReturn(String ss) { System.out.println(ss + " Enter getResultOfConnectDBNoReturn ..."); } public String getResultOfConnectDB() { return "haha, Really went to the database"; } }
需要测试的类:
IUserService.java
package com.github.mock.simple.user; public interface IUserService { public String testedMehtod(); }
UserServiceImpl.java
package com.github.mock.simple.user.impl; import org.springframework.stereotype.Service; import com.github.mock.simple.user.IUserService; import com.github.mock.simple.vo.ProcessDB; @Service public class UserServiceImpl implements IUserService { @Override public String testedMehtod(){ System.out.println("Enter UserServiceImpl testedMehtod ..."); ProcessDB processDB = new ProcessDB("BB"); processDB.getResultOfConnectDBNoReturn("AA"); return processDB.getResultOfConnectDB(); } }
BussinessService.java
package com.github.mock.simple.user.impl; import com.github.mock.simple.vo.ProcessDB; public class BussinessService { public String testedMehtod() { System.out.println("Enter BussinessService testedMehtod ..."); ProcessDB processDB = new ProcessDB("BB"); processDB.getResultOfConnectDBNoReturn("AA"); return processDB.getResultOfConnectDB(); } }
测试类:
MockSpringSimpleTest.java
package com.github.mock.simple.test; import java.text.MessageFormat; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunnerDelegate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.github.mock.simple.user.IUserService; import com.github.mock.simple.user.impl.BussinessService; import com.github.mock.simple.user.impl.UserServiceImpl; import com.github.mock.simple.vo.ProcessDB; @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)//Spring上下文 @PrepareForTest({BussinessService.class,UserServiceImpl.class}) @ContextConfiguration(locations = {"classpath:applicationContext-mock-inject.xml"}) public class MockSpringSimpleTest { //使用Spring上下文 @Autowired IUserService userService; @Mock ProcessDB processDB; //不使用Spring上下文时,使用该注解 @InjectMocks private BussinessService bussinessService; @Before public void initMocks() throws Exception { MockitoAnnotations.initMocks(this); //ReflectionTestUtils.setField(userService, "processDB", processDB); PowerMockito.whenNew(ProcessDB.class).withArguments("BB").thenReturn(processDB); // PowerMockito.whenNew(ProcessDB.class).withNoArguments().thenReturn(processDB); } @Test public void mockConnectDB() { String aa = "haha, everything is fake"; PowerMockito.when(processDB.getResultOfConnectDB()).thenReturn(aa); PowerMockito.doNothing().when(processDB).getResultOfConnectDBNoReturn("AA"); System.out.println(bussinessService.testedMehtod()); Assert.assertEquals("haha, everything is fake", bussinessService.testedMehtod()); } @Test public void mockConnectDB2() { try { String aa = "haha, everything is fake"; PowerMockito.when(processDB.getResultOfConnectDB()).thenReturn(aa); PowerMockito.doNothing().when(processDB).getResultOfConnectDBNoReturn("AA"); System.out.println(userService.testedMehtod()); Assert.assertEquals("haha, everything is fake", userService.testedMehtod()); } catch (Exception ex) { System.out.println("--- getMessage ---"); System.out.println(ex.getMessage()); System.out.println(); System.out.println("--- toString ---"); System.out.println(ex.toString()); System.out.println(); // System.out.println("--- printStackTrace ---"); // StringWriter stringWriter = new StringWriter(); // PrintWriter printWriter = new PrintWriter(stringWriter); // ex.printStackTrace(printWriter); // System.out.println(stringWriter.toString()); // System.out.println(); System.out.println("--- printStackTrace DIY ---"); System.out.println(ex.getClass().getName() + ": " + ex.getMessage()); StringBuilder sbException = new StringBuilder(); for (StackTraceElement ele : ex.getStackTrace()) { sbException.append(MessageFormat.format("\tat {0}.{1}({2}:{3})\n", ele.getClassName(), ele.getMethodName(), ele.getFileName(), ele.getLineNumber()));; } System.out.println(sbException); sbException = null; // stringWriter = null; // printWriter = null; } } }
扫描注入xml
最后applicationContext-mock-inject.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.github.mock.simple"/> </beans>
对于没有实现类,但又被依赖的接口,在applicationContext-mock-inject.xml添加如下内容 (本文不需要):
<bean name="iXxService" class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.github.mock.simple.api.IXxService"/> </bean>
同时在测试类里面添加下面的代码:
@Mock iXxService iXxService;
在 @Before里面添加下面的代码
ReflectionTestUtils.setField(userService, "iXxService", iXxService);
测试结果
PowerMockito的使用技巧
当IT中有些依赖组件无法正常集成,需要mock支持测试,可以使用power mockito。
特别注意:
当对一个对象进行powermockito,应该在prepare方法,统一mock这个对象。然后在其他方法,分别进行调用when,否则,多个方法内进行mock,会出错。
比如有个 Service处于IT case的底层,普通的mock根本mock不进去,但我们又不能为了集成测试,为这个testcase单独开一个口子,注入mock对象。power mockito强大的mock能力在这里可以用上。
比如:
我的mock对象impalaService它在schmaMessagehandler类里new出来的,则需要加上注解。
首先在test 类的开头,加上注解头部,头部类是mock对象所在类。
@RunWith(PowerMockRunner.class) @PrepareForTest({HttpClient.class,SchemaMessageHandler.class})
其次:
PooledImpalaService impalaService = PowerMockito.mock(PooledImpalaService.class); PowerMockito.whenNew(PooledImpalaService.class).withArguments((ConfigurationanyObject()).thenReturn(impalaService); doNothing().when(impalaService).createTable(anyString(),(Schema) anyObject());
使用powermockito,注意在用any()参数时候,比如
doNothing().when(impalaService).createTable(anyString(),(Schema) anyObject());
参数列表中,只要一个使用了any(),any****,则所有参数都要用any相关的参数,否则mock不成功。
总的来说,在it当中,只有你想mock一个对象,一定可以,比如你在A类中用到了B类,那么在prepareForTest中增加A类的注解。
如下:
@PrepareForTest({A.class})然后,在it中 声明一个B类,B b = PowerMockito.mock(B.class);这时候,就可以指定b的方法的返回值,或 PowerMockit.doNothing().when(b).方法名(),让该方法什么也不做。
最后,再讲A实例化。PowerMockit是讲究mock设置顺序的。一定要注意。