• PowerMock测试


    EasyMock可以满足单元测试中的大部分需求,但是由于动态代理是使用了面向对象的继承和多态特性,JDK自身的动态代理只针对接口进行代理,其本质是为接口生成一个实现类,而CGLIB可以针对类进行代理,其本质是将类自身作为基类。

    如果遇到了静态、final类型的类和方法,以及私有方法,EasyMock的动态代理局限性使得无法测试这些特性情况。

    PowerMock是在EasyMock基础上进行扩展(只是补充,不是替代),使用了字节码操作技术直接对生成的字节码类文件进行修改,从而可以方便对静态,final类型的类和方法进行Mock,还可以对私有方法进行Mock,更可以对类进行部分Mock。

    PowerMock的工作过程和EasyMock类似,不同之处在于需要在类层次声明@RunWith(PowerMockRunner.class)注解,以确保使用PowerMock框架引擎执行单元测试。

    通过如下方式在maven添加PowerMock相关依赖:

    [html] view plain copy 在CODE上查看代码片派生到我的代码片
    1. <dependency>  
    2.       <groupId>org.powermock</groupId>  
    3.       <artifactId>powermock-api-easymock</artifactId>  
    4.       <version>1.5.1</version>  
    5.       <scope>test</scope>  
    6.     </dependency>  
    7.     <dependency>  
    8.       <groupId>org.powermock</groupId>  
    9.       <artifactId>powermock-module-junit4</artifactId>  
    10.       <version>1.5.1</version>  
    11.       <scope>test</scope>  
    12.     </dependency>  


    例子如下:

    (1).Miock final类的静态方法:

    如果测试代码中使用到了java.lang.System类,代码如下:

    [java] view plain copy 在CODE上查看代码片派生到我的代码片
    1. public class SystemPropertyMockDemo {       
    2.     public String getSystemProperty() throws IOException {       
    3.         return System.getProperty("property");       
    4.     }       
    5. }  

    如果对System.getProperty()方法进行Mock,代码如下:

    [java] view plain copy 在CODE上查看代码片派生到我的代码片
    1. @RunWith(PowerMockRunner.class)       
    2. @PrepareForTest({SystemPropertyMockDemo.class})//声明要Mock的类       
    3. public class SystemPropertyMockDemoTest {       
    4.     @Test      
    5.     public void demoOfFinalSystemClassMocking() throws Exception {       
    6.         PowerMock.mockStatic(System.class);//Mock静态方法       
    7.         EasyMock.expect(System.getProperty("property")).andReturn("my property");//录制Mock对象的静态方法       
    8.         PowerMock.replayAll();//重放Mock对象       
    9.         Assert.assertEquals("my property",       
    10.                                   new SystemPropertyMockDemo().getSystemProperty());       
    11.         PowerMock.verifyAll();//验证Mock对象       
    12.     }       
    13. }   

    非final类的静态方法代码相同,注意(上述代码只能在EasyMock3.0之后版本正常运行)

    如果要在EasyMock3.0之前版本正常Mock final类的静态方法,需要使用PowerMockito,

    通过如下方式在maven中添加PowerMockito相关依赖:

    [html] view plain copy 在CODE上查看代码片派生到我的代码片
    1. <dependency>  
    2.       <groupId>org.powermock</groupId>  
    3.       <artifactId>powermock-api-mockito</artifactId>  
    4.       <version>1.5.1</version>  
    5.       <scope>test</scope>  
    6.     </dependency>  


    代码如下:

    [java] view plain copy 在CODE上查看代码片派生到我的代码片
    1. @RunWith(PowerMockRunner.class)       
    2. @PrepareForTest({SystemPropertyMockDemo.class})       
    3. public class SystemPropertyMockDemoTest {       
    4.     @Test      
    5.     public void demoOfFinalSystemClassMocking() throws Exception {       
    6.         PowerMockito.mockStatic(System.class);       
    7.         PowerMockito.when(System.getProperty("property")).thenReturn("my property");       
    8.         PowerMock.replayAll();       
    9.         Assert.assertEquals("my property",       
    10.                                   new SystemPropertyMockDemo().getSystemProperty());       
    11.         PowerMock.verifyAll();       
    12.     }       
    13. }  

    注意:  

    对于JDK的类如果要进行静态或final方法Mock时,@PrepareForTest()注解中只能放被测试的类,而非JDK的类,如上面例子中的SystemPropertyMockDemo.class。  

    对于非JDK的类如果需要进行静态活final方法Mock时, @PrepareForTest()注解中直接放方法所在的类,若上面例子中的System不是JDK的类,则可以直接放System.class。

    @PrepareForTest({......}) 注解既可以加在类层次上(对整个测试文件有效),也可以加在测试方法上(只对测试方法有效)。

    (2).Mock非静态的final方法:

    被测试代码如下:

    [java] view plain copy 在CODE上查看代码片派生到我的代码片
    1. public class ClassDependency {          
    2.     public final boolean isAlive() {    
    3.         return false;    
    4.     }    
    5. }  
    6.   
    7. public class ClassUnderTest{  
    8.     public boolean callFinalMethod(ClassDependency refer) {    
    9.         return refer.isAlive();    
    10.     }  
    11. }  

    使用PowerMock的测试代码如下:

    [java] view plain copy 在CODE上查看代码片派生到我的代码片
    1. @RunWith(PowerMockRunner.class)          
    2. public class FinalMethodMockDemoTest {       
    3.     @Test    
    4.     @PrepareForTest(ClassDependency.class)    
    5.     public void testCallFinalMethod() {    
    6.         ClassDependency depencency = PowerMock.createMock(ClassDependency.class); //创建Mock对象  
    7.         ClassUnderTest underTest = new ClassUnderTest();    
    8.         EasyMock.expect(depencency.isAlive()).andReturn(true);    
    9.         PowerMock.replayAll();  
    10.         Assert.assertTrue(underTest.callFinalMethod(depencency));    
    11.        PowerMock.verifyAll();  
    12.     }  
    13. }  

    (3)部分Mock和私有方法Mock:

    如果被测试类某个方法不太容易调用,可以考虑只对该方法进行Mock,而其他方法全部使用被测试对象的真实方法,可以考虑使用PowerMock的部分Mock,被测试代码如下:

    [java] view plain copy 在CODE上查看代码片派生到我的代码片
    1. public class DataService {  
    2.         public boolean replaceData(final String dataId, final byte[] binaryData) {  
    3.                 return modifyData(dataId, binaryData);  
    4.         }  
    5.         public boolean deleteData(final String dataId) {  
    6.                 return modifyData(dataId, null);  
    7.         }  
    8.   
    9.         private boolean modifyData(final String dataId, final byte[] binaryData) {  
    10.                 return true;  
    11.         }  
    12. }   

    只对modifyData方法进行Mock,而其他方法调用真实方法,测试代码如下:

    [java] view plain copy 在CODE上查看代码片派生到我的代码片
    1. @RunWith(PowerMockRunner.class)   
    2. @PrepareForTest(DataService.class)  
    3. public class DataServiceTest {  
    4. @Test  
    5. public void testReplaceData() throws Exception {  
    6.         DataService tested = PowerMock.createPartialMock(DataService.class, “modifyData”);//创建部分mock对象,只对modifyData方法Mock  
    7.         PowerMock.expectPrivate(tested, “modifyData”, “id”, null).andReturn(true);//录制私有方法  
    8.         PowerMock.replay(tested);  
    9.         assertTrue(tested.deleteData(“id”));  
    10.         PowerMock.verify(tested);  
    11. }  
    12. }   

    部分Mock在被测试方法的依赖在同一个类,且不容易创建时比较有用。

    个人认为私有方法的Mock意义不是很大,完全可以使用反射机制直接调用。

    (4).调用对象的构造方法Mock对象:

    在被测试方法内部调用构造创建了一个对象很常见,被测试代码如下:

    [java] view plain copy 在CODE上查看代码片派生到我的代码片
    1. public class PersistenceManager {  
    2.         public boolean createDirectoryStructure(String directoryPath) {  
    3.                 File directory = new File(directoryPath);  
    4.                 if (directory.exists()) {  
    5.                         throw new IllegalArgumentException(""" + directoryPath + "" already exists.");  
    6.                 }  
    7.                 return directory.mkdirs();  
    8.         }  
    9. }   

    创建文件操作(new File(path))依赖与操作系统底层实现,如果给定的路径不合法,将会出现异常导致测试无法正常覆盖,此时需要使用PowerMock的提供的调用构造方法创建Mock对象,测试代码如下:

    [java] view plain copy 在CODE上查看代码片派生到我的代码片
    1. @RunWith(PowerMockRunner.class)  
    2. @PrepareForTest( PersistenceManager.class )  
    3. public class PersistenceManagerTest {  
    4.            @Test  
    5.         public void testCreateDirectoryStructure_ok() throws Exception {  
    6.                 File fileMock = PowerMock.createMock(File.class);  
    7.                 PersistenceManager tested = new PersistenceManager();  
    8.                 PowerMock.expectNew(File.class, "directoryPath").andReturn(fileMock);  
    9.                 EasyMock.expect(fileMock.exists()).andReturn(false);  
    10.                 EasyMock.expect(fileMock.mkdirs()).andReturn(true);  
    11.                 PowerMock.replay(fileMock, File.class);  
    12.                 assertTrue(tested.createDirectoryStructure("directoryPath"));  
    13.                 PowerMock.verify(fileMock, File.class);  
    14.         }  
    15. }   

    也可以使用更简便的方法:

    FilefileMock = PowerMock.createMockAndExpectNew(File.class,“directoryPath”);

    通过EasyMock+PowerMock,开发中绝大部分的方法都可以被测试完全覆盖。

    更多关于PowerMock的用法和参考文档请参考PowerMock官方网址:

    https://code.google.com/p/powermock/

  • 相关阅读:
    MySQL5.7安装详细教程
    Java之GUI编程
    Java基础
    生成JavaDoc文档
    SpringtMVC运行流程:@RequestMapping 方法中的 Map、HttpServletRequest等参数信息是如何封装和传递的(源码理解)
    SpringCache @Cacheable 在同一个类中调用方法,导致缓存不生效的问题及解决办法
    Spring源码学习:第1步--在Spring源码中添加最简单的Demo代码
    Spring源码学习:第2步--使用SLF4j+Log4j日志框架替换掉其自身的commons-logging日志框架
    Spring源码学习:第0步--环境准备
    JasperReport报表
  • 原文地址:https://www.cnblogs.com/xinziyublog/p/5651146.html
Copyright © 2020-2023  润新知