powermockito学习笔记

author: yzh

date:2025/07/03

一、背景介绍

在 Java 单元测试中,使用 Mockito 很方便地可以 mock 接口、普通类等。但当你遇到以下情况时:

  • 想要 mock static 方法

  • 想要 mock final 类或方法

  • 想要 mock 构造函数(new)

  • 想要 mock 私有方法

普通的 Mockito 就力不从心了。这时就需要借助 PowerMock,而 PowerMockito 是 PowerMock 对 Mockito 风格的封装,适合使用 Mockito 的开发者。

Mockito:最流行的 Mock 框架,简单易用。最新版本的Mockito能模拟静态方法,但是不能模拟私有方法

PowerMockito:扩展 Mockito,支持模拟静态方法、构造函数等。

二、配置使用

PowerMockito 底层是通过字节码修改(instrumentation + classloader)实现的,默认情况下,PowerMockito 与 JUnit4 原生兼容,而与 TestNG 并非直接集成。 不过,通过设置 @ObjectFactory 和使用 PowerMockObjectFactory,是可以让 PowerMockito 支持 TestNG 的。

<dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>3.12.4</version>
        </dependency>
<!-- PowerMock for TestNG -->
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-testng</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>
​
<!-- TestNG -->
<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.7.0</version>
    <scope>test</scope>
</dependency>

这里要注意TestNG的版本问题,7.x版本过高可能会出现一些问题,将版本将为6.x即可

<!-- TestNG -->
<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>6.14.3</version>
    <scope>test</scope>
</dependency>

但是运行时可能还会有一点问题,PowerMock 使用反射访问 Object.clone() 这样的底层方法,但JDK9+(包括你用的 JDK17)引入了强封装机制,禁止非法反射访问 JDK 内部类或方法。所以可以切换JDK到1.8或者在JVM参数中强行开放模块。

基础配置模板

@PrepareForTest({TargetClass.class})
public class XxxTest {
​
    @ObjectFactory
    public IObjectFactory getObjectFactory() {
        return new PowerMockObjectFactory();
    }
​
    // 你的@Test方法
}
注解/方法作用
@PrepareForTest标记需要被 mock 的类(static、构造等)
@ObjectFactory告诉 TestNG 使用 PowerMock 的测试执行机制
PowerMockObjectFactoryPowerMock 提供的 TestNG ObjectFactory

TestNG 是负责创建测试类对象的(不是业务对象),而 PowerMock 通过自定义的 ObjectFactory(PowerMockObjectFactory)接管这个过程,在对象创建的同时注入字节码代理,确保 mock 能生效。

三、简单例子

注解相关使用说明

注解作用
@RunWith(PowerMockRunner.class)使用 PowerMockRunner 替代 JUnit 的默认运行器
@PrepareForTest({Class1.class, Class2.class})指定哪些类需要进行字节码增强(用于 mock 静态方法、final 方法、构造函数等)
@PowerMockIgnore("javax.management.*")忽略某些类的加载冲突,避免异常
@Mock创建 mock 对象
@InjectMocks自动注入 @Mock 创建的对象到被测试类中
@Spy对真实对象的部分方法进行 mock(其余保留原始逻辑)
@Captor声明参数捕获器(ArgumentCaptor)
@Before在每个测试方法执行前调用(初始化 mock 用)

Mockito常用方法说明

方法用途示例
Mockito.mock(Class)创建一个 mock 对象UserDao dao = Mockito.mock(UserDao.class);
Mockito.when(xxx).thenReturn(x)设置 mock 对象调用方法的返回值Mockito.when(dao.read(1)).thenReturn(user);
Mockito.verify(xxx)验证 mock 方法是否被调用Mockito.verify(dao).read(1);
Mockito.doNothing().when(xxx).method()mock void 方法(无返回值)Mockito.doNothing().when(service).deleteUser(1);
Mockito.doReturn(x).when(xxx).method()替代 when(...).thenReturn(...) 用于 spy 对象Mockito.doReturn("Mock").when(spyObj).getData();
Mockito.spy(Object)创建部分 mock 的对象(保留真实逻辑)UserService spyService = Mockito.spy(realService);
Mockito.any() / Mockito.anyInt() 等参数通配符Mockito.when(dao.read(Mockito.anyInt())).thenReturn(user);
Mockito.times(n)验证方法调用次数Mockito.verify(dao, Mockito.times(1)).read(1);

PowerMockito 常用方法说明

方法用途示例
PowerMockito.mockStatic(Class)mock 静态类PowerMockito.mockStatic(StaticUtil.class);
PowerMockito.when(Class.method()).thenReturn(x)mock 静态方法PowerMockito.when(StaticUtil.getName()).thenReturn("Mock");
PowerMockito.doReturn(x).when(obj, "methodName", params...)mock 私有方法PowerMockito.doReturn("mock").when(service, "privateMethod");
PowerMockito.whenNew(Class).withAnyArguments().thenReturn(obj)mock 构造函数PowerMockito.whenNew(User.class).withAnyArguments().thenReturn(mockUser);
PowerMockito.spy(obj)类似 Mockito.spy,用于 mock 私有/静态等UserService spyService = PowerMockito.spy(realService);

示例 一:Mock 静态方法

public class StaticUtil {
    public static String getName() {
        return "真实值";
    }
}
​
//测试类
@PrepareForTest({StaticUtil.class})
public class StaticUtilTest {
​
    @ObjectFactory
    public IObjectFactory getObjectFactory() {
        return new PowerMockObjectFactory();
    }
​
    @Test
    public void testStaticMethod() {
        PowerMockito.mockStatic(StaticUtil.class);
        Mockito.when(StaticUtil.getName()).thenReturn("测试值");
        
        //注意这里不是返回方法中的值而是mock返回的值,相当于是我们模拟一个返回结果
        String result = StaticUtil.getName();
        Assert.assertEquals(result, "Mock值");
    }
}

相当于模拟操作,如果调用了getName方法,就得到返回值,然后再使用断言进行测试进行判断,这里因为返回值和断言中的值不一样,所以会报错。

示例二:Mock 构造函数

public class Person {
    public String getName() {
        return "真实姓名";
    }
}
​
public class PersonService {
    public String createPerson() {
        Person p = new Person(); // 这里 new 出来的对象我们想 mock
        return p.getName();
    }
}
@PrepareForTest({PersonService.class})
public class PersonServiceTest {
​
    @ObjectFactory
    public IObjectFactory getObjectFactory() {
        return new PowerMockObjectFactory();
    }
​
    @Test
    public void testNewObjectMock() throws Exception {
        Person mockPerson = Mockito.mock(Person.class);
        // 设置调用 getName() 返回 "Mock人名"
        Mockito.when(mockPerson.getName()).thenReturn("Mock人名");
        // 当代码执行 new Person() 时,返回 mockPerson,而不是真实对象
        PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(mockPerson);
​
        // 被测逻辑
        PersonService service = new PersonService();
        String result = service.createPerson();
        Assert.assertEquals(result, "Mock人名");
    }
}

示例三:Mock 私有方法

public class PrivateService {
    public String call() {
        return getSecret(); // 私有方法
    }
​
    private String getSecret() {
        return "真实密钥";
    }
}
@PrepareForTest({PrivateService.class})
public class PrivateServiceTest {
​
    @ObjectFactory
    public IObjectFactory getObjectFactory() {
        return new PowerMockObjectFactory();
    }
​
    @Test
    public void testPrivateMethodMock() throws Exception {
        // 这里spy相当于是调用方法并执行对应操作,而mock只是调用但是不执行
        PrivateService service = PowerMockito.spy(new PrivateService());
        //这里使用PowerMockito,所以是先return后判断when
        PowerMockito.doReturn("Mock密钥").when(service, "getSecret");
​
        String result = service.call();
​
        Assert.assertEquals(result, "Mock密钥");
    }
}

when(obj, "methodName") 中每个参数的含义

"Mock值":你要让方法返回的值

service:被测试对象(通常是 spy 出来的对象)

"getSecret":你要 mock 的方法名(注意是字符串)

根据方法名来进行判断是否调用了对应方法进行匹配判断,这里call会调用getSecret私有方法,所以断言是正确的

示例四:Mock final 类

public final class FinalUtil {
    public final String getToken() {
        return "真实 token";
    }
}
@PrepareForTest({FinalUtil.class})
public class FinalUtilTest {

    @ObjectFactory
    public IObjectFactory getObjectFactory() {
        return new PowerMockObjectFactory();
    }

    @Test
    public void testFinalClassMock() {
        FinalUtil util = PowerMockito.mock(FinalUtil.class);
        Mockito.when(util.getToken()).thenReturn("MockToken");

        Assert.assertEquals(util.getToken(), "MockToken");
    }
}

示例五:MVC简单测试

public class User {
    private int id;
    private String name;

    // 构造方法、getter、setter
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() { return id; }
    public String getName() { return name; }
    public void setId(int id) { this.id = id; }
    public void setName(String name) { this.name = name; }
}
public class UserDao {
    private final Map<Integer, User> database = new HashMap<>();

    public void create(User user) {
        database.put(user.getId(), user);
    }

    public User read(int id) {
        return database.get(id);
    }

    public void update(User user) {
        database.put(user.getId(), user);
    }

    public void delete(int id) {
        database.remove(id);
    }

    public List<User> list() {
        return new ArrayList<>(database.values());
    }
}
public class UserService {

    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser(User user) {
        userDao.create(user);
    }

    public User getUser(int id) {
        return userDao.read(id);
    }

    public void updateUser(User user) {
        userDao.update(user);
    }

    public void deleteUser(int id) {
        userDao.delete(id);
    }

    public List<User> getAllUsers() {
        return userDao.list();
    }
}

因为这里不涉及上面那些特殊情况,所以使用mockito来进行mock测试也可以

public class UserServiceTest {

    @Mock
    private UserDao userDao;

    @InjectMocks
    private UserService userService;

    @BeforeMethod
    public void setUp() {
        //注入标记的对象
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testAddUser() {
        User user = new User(1, "Tom");
        when(userDao.read(1)).thenReturn(user);
        userService.addUser(user);
        //验证方法是否被执行
        verify(userDao).create(user);
        User result = userService.getUser(1);
        //验证返回结果是否符合预期
        Assert.assertEquals(result.getName(),"Tom");
    }

    @Test
    public void testGetUSer(){
        //这里mock返回的数据是我们指定的,并不是真正存在于数据库中的,mock只是用于验证方法是否被调用
        User mockUSer = new User(1, "Alice");
        when(userDao.read(1)).thenReturn(mockUSer);
        User result = userService.getUser(1);
        verify(userDao).read(1);
        Assert.assertEquals(result.getName(),"Alice");
    }

    @Test
    public void testUpdateUSer(){
        User test = new User(1, "test");
        userService.updateUser(test);
        verify(userDao).update(test);
    }

    @Test
    public void testDeleteUser() {
        userService.deleteUser(1);
        verify(userDao).delete(1);
    }

    @Test
    public void testGetAllUsers(){
        List<User> mock = Arrays.asList(
                new User(1,"Tom"),
                new User(2,"Alice")
        );
        when(userDao.list()).thenReturn(mock);

        List<User> userList = userService.getAllUsers();
        Assert.assertEquals(userList.size(),2);
        Assert.assertEquals(userList.get(0).getName(),"Tom");
    }

}
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐