PowerMock 入门

2023-10-29

介绍

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.class);
        // mock File的构造函数
PowerMockito.whenNew(File.class).withArguments(directoryPath).thenReturn(directoryMock);
        // 录制行为
        PowerMockito.when(directoryMock.exists()).thenReturn(false);
        PowerMockito.when(directoryMock.mkdirs()).thenReturn(true);
        // 调用测试
        assert(new NewFileExample().createDirectoryStructure(directoryPath));
        // 验证行为
        PowerMockito.verifyNew(File.class).withArguments(directoryPath);
    }
}
class NewFileExample {
    public boolean createDirectoryStructure(String path) {
        DirectoryStructure directoryStructure = new DirectoryStructure();
        return directoryStructure.create(path);
    }
}
class DirectoryStructure {
    public boolean create(String directoryPath) {
        File directory = new File(directoryPath);
        if (directory.exists()) {
            throw new IllegalArgumentException("\"" + directoryPath + "\" already exists.");
        }
        return directory.mkdirs();
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PowerMock 入门 的相关文章

  • Play框架运行应用程序问题

    每当我尝试运行使用以下命令创建的新 Web 应用程序时 我都会收到以下错误Play http www playframework org Error occurred during initialization of VM Could no
  • 如何找到给定字符串的最长重复子串

    我是java新手 我被分配寻找字符串的最长子字符串 我在网上研究 似乎解决这个问题的好方法是实现后缀树 请告诉我如何做到这一点或者您是否有任何其他解决方案 请记住 这应该是在 Java 知识水平较低的情况下完成的 提前致谢 附 测试仪字符串
  • 给定两个 SSH2 密钥,我如何检查它们是否属于 Java 中的同一密钥对?

    我正在尝试找到一种方法来验证两个 SSH2 密钥 一个私有密钥和一个公共密钥 是否属于同一密钥对 我用过JSch http www jcraft com jsch 用于加载和解析私钥 更新 可以显示如何从私钥 SSH2 RSA 重新生成公钥
  • Android:捕获的图像未显示在图库中(媒体扫描仪意图不起作用)

    我遇到以下问题 我正在开发一个应用程序 用户可以在其中拍照 附加到帖子中 并将图片保存到外部存储中 我希望这张照片也显示在图片库中 并且我正在使用媒体扫描仪意图 但它似乎不起作用 我在编写代码时遵循官方的Android开发人员指南 所以我不
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • Spring Data JPA 应用排序、分页以及 where 子句

    我目前正在使用 Spring JPA 并利用此处所述的排序和分页 如何通过Spring data JPA通过排序和可分页查询数据 https stackoverflow com questions 10527124 how to query
  • 我可以使用 HSQLDB 进行 junit 测试克隆 mySQL 数据库吗

    我正在开发一个 spring webflow 项目 我想我可以使用 HSQLDB 而不是 mysql 进行 junit 测试吗 如何将我的 mysql 数据库克隆到 HSQLDB 如果您使用 spring 3 1 或更高版本 您可以使用 s
  • Mockito when().thenReturn 不必要地调用该方法

    我正在研究继承的代码 我编写了一个应该捕获 NullPointerException 的测试 因为它试图从 null 对象调用方法 Test expected NullPointerException class public void c
  • 无法解析插件 Java Spring

    我正在使用 IntelliJ IDEA 并且我尝试通过 maven 安装依赖项 但它给了我这些错误 Cannot resolve plugin org apache maven plugins maven clean plugin 3 0
  • 十进制到八进制的转换[重复]

    这个问题在这里已经有答案了 可能的重复 十进制转换错误 https stackoverflow com questions 13142977 decimal conversion error 我正在为一个类编写一个程序 并且在计算如何将八进
  • Java TestNG 与跨多个测试的数据驱动测试

    我正在电子商务平台中测试一系列商店 每个商店都有一系列属性 我正在考虑对其进行自动化测试 是否有可能有一个数据提供者在整个测试套件中提供数据 而不仅仅是 TestNG 中的测试 我尝试不使用 testNG xml 文件作为机制 因为这些属性
  • 如何将 pfx 文件转换为 jks,然后通过使用 wsdl 生成的类来使用它来签署传出的肥皂请求

    我正在寻找一个代码示例 该示例演示如何使用 PFX 证书通过 SSL 访问安全 Web 服务 我有证书及其密码 我首先使用下面提到的命令创建一个 KeyStore 实例 keytool importkeystore destkeystore
  • Java Integer CompareTo() - 为什么使用比较与减法?

    我发现java lang Integer实施compareTo方法如下 public int compareTo Integer anotherInteger int thisVal this value int anotherVal an
  • Eclipse Java 远程调试器通过 VPN 速度极慢

    我有时被迫离开办公室工作 这意味着我需要通过 VPN 进入我的实验室 我注意到在这种情况下使用 Eclipse 进行远程调试速度非常慢 速度慢到调试器需要 5 7 分钟才能连接到远程 jvm 连接后 每次单步执行断点 行可能需要 20 30
  • 如何从泛型类调用静态方法?

    我有一个包含静态创建方法的类 public class TestClass public static
  • 声明的包“”与预期的包不匹配

    我可以编译并运行我的代码 但 VSCode 中始终显示错误 早些时候有一个弹出窗口 我不记得是什么了 我点击了 全局应用 从那以后一直是这样 Output is there but so is the error The declared
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 有没有办法为Java的字符集名称添加别名

    我收到一个异常 埋藏在第 3 方库中 消息如下 java io UnsupportedEncodingException BIG 5 我认为发生这种情况是因为 Java 没有定义这个名称java nio charset Charset Ch
  • 按日期对 RecyclerView 进行排序

    我正在尝试按日期对 RecyclerView 进行排序 但我尝试了太多的事情 我不知道现在该尝试什么 问题就出在这条线上适配器 notifyDataSetChanged 因为如果我不放 不会显示错误 但也不会更新 recyclerview

随机推荐