小白也能学会的单元测试:PowerMockito + TestNG 完整教程!

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 的测试执行机制 |
PowerMockObjectFactory | PowerMock 提供的 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");
}
}
更多推荐

所有评论(0)