PowerMock 入门

Posted Firm陈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PowerMock 入门相关的知识,希望对你有一定的参考价值。

介绍

PowerMock是一个Java模拟框架,用于解决测试问题。

PowerMock 由Mockito和EasyMock两部分API构成,它必须要依赖测试框架。

当前PowerMock支持Junit和TestNG.两种测试框架。

针对Junit又有三种不同的执行器对应JUnit4.4+、JUnit4.0-4.3和JUnit 3。

TestNG的执行器实现只有一个版本,但是需要依赖TestNG5.11+版本。

Mock测试

Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。

好处-- 模拟数据
在使用Junit进行单元测试时,不想让测试数据进入数据库,可以使用PowerMock,拦截数据库操作,并模拟返回参数。

好处–减少依赖
代码存在如下依赖:

当需要测试A类的时候,如果没有Mock,则需要把整个依赖树都构建出来,而使用Mock的话就可以将结构分解开

包导入
Junit 4.4+ (不同包导入会有差异)

<!-- PowerMock JUnit 4.4+ Module -->
<dependency>
  <groupId>org.powermock</groupId>
  <artifactId>powermock-module-junit4</artifactId>
  <version>2.0.0</version>
  <scope>test</scope>
</dependency>
<!-- PowerMock Mockito2 API -->
<dependency>
  <groupId>org.powermock</groupId>
  <artifactId>powermock-api-mockito2</artifactId>
  <version>2.0.0</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
</dependency>

示例

/***********************Prepare****************************/
public interface MockMapper 	  
	public int count(MockModel model);

 
@Service
public class MockServiceImpl 	  
	@Autowired	  
	private MockMapper mockMapper;		  

	public int count(MockModel model) 		    
		return mockMapper.count(model);	  
	

/*****************************************************/
@RunWith(PowerMockRunner.class) // 告诉JUnit使用PowerMockRunner进行测试
@PrepareForTest(MockUtil.class) // 所有需要测试的类列在此处,适用于模拟final类或有final, private, static, native方法的类
@PowerMockIgnore("javax.management.*") //为了解决使用powermock后,提示classloader错误
public class MockExample 
    // 将@Mock注解的示例注入进来
    @InjectMocks
    private MockServiceImpl mockService;
    @Mock
    private MockMapper mockMapper;
    
    /**
     * mock普通方法
     */
    @Test
    public void testSelectAppAdvertisementList() 
        MockModel model = new MockModel();
        PowerMockito.when(mockMapper.count(model)).thenReturn(2);
        
        Assert.assertEquals(2, mockService.count(model));
    


常用注解

@RunWith(PowerMockRunner.class) // 告诉JUnit使用PowerMockRunner进行测试
@PrepareForTest(RandomUtil.class) // 所有需要测试的类列在此处,适用于模拟final类或有final, private, static, native方法的类
@PowerMockIgnore("javax.management.*") //为了解决使用powermock后,提示classloader错误

@RunWith(PowerMockRunner.class)
该注解需要在测试类上添加,告诉JUnit使用PowerMock执行器,一般来说都添加上。

@PrepareForTest(UserController.class)
所有需要测试的类,列在此处,以逗号分隔。测试静态方法、私有方法、final方法、使用System/Bootstrap类加载器加载的类和mock构造方法时需要添加该注解。
注意:@PrepareForTest中包含的类不会被LLT统计到代码覆盖。

@PowerMockIgnore(“javax.management.*”)
为了解决使用powermock后,提示classloader错误。

常用行为录制模式

when(…).thenXXX()
doXXX(…).when(mockObj)

使用方法
(1)在测试类上加入@RunWith(PowerMockRunner.class)(该注解告诉JUnit使用PowerMock执行器)和@ PrepareForTest(Static.class)注解.

(2)使用PowerMock.mockStatic(Static.class)可以mock Static类中所有的静态方法。也可以使用PowerMockito.spy(class)方法来对特定的方法mock.

(3)调用Mockito.when()来录制行为。

验证行为
要验证静态方法是否有被执行,可以使用PowerMockito.verifyStatic(Mockito.times(1)),每验证一个函数都需要先调用一次PowerMockito.verifyStatic(…)函数:

(1)调用PowerMockito.verifyStatic()方法开始验证行为,不传参数默认是验证调用一次,要校验调用多次传入Mockito.times(…)即可。

(2)PowerMockito.verifyStatic()之后,调用需要验证的静态方法即可完成验证。

PowerMockito.verifyStatic(); // 1
ClassThatContainsStaticMethod. getUserName (param); // 2

使用参数匹配
Mockito的参数匹配仍旧适用于PowerMock

PowerMockito.verifyStatic();
Static.thirdStaticMethod(Mockito.anyInt());

抛出异常
非私有方法

PowerMockito.doThrow(new ArrayStoreException("Mock error")).when(StaticService.class);

私有方法
由于不能直接访问方法,可以将方法名以字符串的形式告诉powermock进行调用

when(tested, "methodToExpect", argument).thenReturn(myReturnValue);
when(tested, "methodToExpect", argument).thenThrow(new ArrayStoreException("Mock error"));

示例
类ClassThatContainsStaticMethod
存在一个静态方法getUserName()返回一个字符串:UserName:id。

package com.cainiaoguoguo.mocktest.staticmethod;

public class ClassThatContainsStaticMethod 

    public static String getUserName(int id)

    

        return "UserName:" + id;

    


对ClassThatContainsStaticMethod.getUserName()进行mock

package com.cainiaoguoguo.mocktest.staticmethod;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.mockito.Mockito;

import org.powermock.api.mockito.PowerMockito;

import org.powermock.core.classloader.annotations.PrepareForTest;

import org.powermock.modules.junit4.PowerMockRunner;


import static org.junit.Assert.*;

 

@RunWith(PowerMockRunner.class)

@PrepareForTest(ClassThatContainsStaticMethod.class)

public class StaticMethodTest 

    @Test

    public void echoUserName() throws Exception 

        final String expectResult = "expectResult";

 

        // mock ClassThatContainsStaticMethod类中所有的静态方法

        PowerMockito.mockStatic(ClassThatContainsStaticMethod.class);

 

        // 改变方法的行为,当调用getUserName的时候返回expectResult

        PowerMockito.when(ClassThatContainsStaticMethod.getUserName(Mockito.anyInt())).thenReturn(expectResult);

        assert(ClassThatContainsStaticMethod.getUserName(123).equals(expectResult));// true

    


首先添加了@RunWith(PowerMockRunner.class)和@PrepareForTest(ClassThatContainsStaticMethod.class)两个注解
然后调用PowerMockito.mockStatic对静态方法进行mock
接下来调用when…then…模式对方法行为进行录制
调用getUserName方法判断返回结果。

综合示例

/************************Prepare****************************/
public class MockUtil 
    private static final Random random = new Random();
    
    public static int nextInt(int bound) 
        return random.nextInt(bound);
    

/***************************************************/

@RunWith(PowerMockRunner.class) // 告诉JUnit使用PowerMockRunner进行测试
@PrepareForTest(MockUtil.class) // 所有需要测试的类列在此处,适用于模拟final类或有final, private, static, native方法的类
@PowerMockIgnore("javax.management.*") //为了解决使用powermock后,提示classloader错误
public class MockExample 
  @Test   
  public void testStaticMethod()      
  	PowerMockito.mockStatic(MockUtil.class);     
  	PowerMockito.when(MockUtil.nextInt(10)).thenReturn(7);     
  	Assert.assertEquals(7, MockUtil.nextInt(10));   
  
 

Mock Final关键字

说明
(1)需要添加@RunWith(PowerMockRunner.class)注解和@PrepareForTest(ClassWithFinal.class)注解
(2)调用PowerMock.mock(ClassWithFinal.class)方法来mock final类的所有方法的,调用该方法将返回一个mock对象(暂且叫他mockObject)。
实例
final类StateHolder
提供一个getState方法返回一个调用其他接口的字符串

package com.cainiaoguoguo.mocktest.finalclass;

public final class StateHolder 

    public String getState() 

        return innerMethod("MSG from StateHolder");

    
    private String innerMethod(String msg) 

        return "INNER: " + msg;

    


需要测试的StateFormatter类
引用了StateHolder,要对StateFormatter类中的getFormattedState()方法进行测试

package com.cainiaoguoguo.mocktest.finalclass;

public class StateFormatter 

    private final StateHolder stateHolder;
    public StateFormatter(StateHolder stateHolder) 
        this.stateHolder = stateHolder;
    
    public String getFormattedState() 

        String safeState = "State information is missing";

        final String actualState = stateHolder.getState();

        if (actualState != null) 
            safeState = actualState;
        
        return safeState;
    

测试类
目的要在调用getFormattedState()方法的过程中让StateHolder的getState()方法返回符合预期的值

package com.cainiaoguoguo.mocktest.finalclass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest(StateHolder.class)
public class StateFormatterTest 
    @Test
    public void getFormattedState() throws Exception 

        final String expectedState = "state";
        
        StateHolder stateHolderMock = mock(StateHolder.class);

        when(stateHolderMock.getState()).thenReturn(expectedState);

        StateFormatter tested = new StateFormatter(stateHolderMock);

        String actualState = tested.getFormattedState();

        assertEquals(expectedState, actualState);
    

注意:
(1)需要添加@RunWith(PowerMockRunner.class)和@PrepareForTest(StateHolder.class)注解;
(2)调用PowerMockito.mock()方法mock需要被拦截的类;
(3)使用when…thenReturn…的模式返回期望得到的值。

局部Mock

说明
PowerMockito.spy():不mock一个类中的所有方法,而是mock部分方法
使用场景:当要真实调用某些方法。
注意:
使用spy来录制行为的时候使用when(…).thenXxx(…)的模式会直接调用原有方法,可能会和预期的结果不符合。正确的使用方式是doXXX(…).when(…).methodToMock(…)。

import org.junit.Test;
import org.powermock.api.mockito.PowerMockito;
import java.util.ArrayList;
import java.util.List;
public class SpyTest 
    @Test
    public void testSpy() 
        List list =  new ArrayList();
        List spy = PowerMockito.spy(list);
        // 会抛出IndexOutOfBoundsException异常,因为会去调用spy.get(0)方法。
        PowerMockito.when(spy.get(0)).thenReturn("foo");
        // 正确的写法如下
        PowerMockito.doReturn("foo").when(spy).get(0);
    

验证行为
普通公有方法
验证方式和静态方法类似,调用Mockito.vertify()就可以进行标准的行为验证

@Test
public void testSpy() 
    List list =  new ArrayList();
    List spy = PowerMockito.spy(list);
    PowerMockito.doReturn("foo").when(spy).get(0);
    spy.get(0);
    spy.get(0);
    Mockito.verify(spy, Mockito.times(2)).get(0); //验证spy.get(0)是否被调用两次

验证私有方法
验证私有方法使用PowerMockito.verifyPrivate()即可,该方法对静态私有方法也适用

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(PartialMockClass.class)
public class SpyTest 
    @Test
    public void verifyPrivate() throws Exception 
        final String expectResult = "MESSAGE";
        String param = "param string";
        PartialMockClass classUnderTest = PowerMockito.spy(new PartialMockClass());
        PowerMockito.doReturn(expectResult).when(classUnderTest, "privateMethod", param);
        // 调用会执行privateMethod
        assert(classUnderTest.publicMethod(param).equals(expectResult));
        // 使用PowerMockito.verify()验证调用
        PowerMockito.verifyPrivate(classUnderTest, Mockito.times(1)).invoke("privateMethod", param);
    

class PartialMockClass 
    public String publicMethod(String msg) 
        return privateMethod(msg);
    
    private String privateMethod(String msg) 
        return "MSG:" + msg;
    

Mock构造函数

说明
能模拟构造函数从而使被测代码中 new 操作返回的对象可以被随意定制
会很大程度的提高单元测试的效率。

使用的形式是whenNew(MyClass.class).with[No|Any]Arguments().thenXXX(…)
示例

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.File;
@RunWith(PowerMockRunner.class)
@PrepareForTest(DirectoryStructure.class)
public class ConstructionTest 
    @Test
    public void testConstruction() throws Exception 
        final String directoryPath = "mocked path";
        File directoryMock = PowerMockito.mock(File.PowerMock+Junit测试小记

解决Jacoco和PowerMock不兼容的问题

使用 Powermock 时出现 NoClassDefFoundError

我们可以使用Mockito和PowerMock出售junit测试类

Spring+JUnit4单元测试入门

PowerMock ECLemma 覆盖问题