1. 概述
枚举(enum)是一种特殊的类,它继承自 Enum
类。枚举由一组静态最终(static final)值组成,这些值不可更改。我们通常用枚举来定义变量可能包含的一组预期值,这样能让代码更易读且不易出错。
但在测试场景中,有时我们需要模拟枚举值。本文将介绍如何使用 Mockito 库模拟枚举,并讨论这种技术的适用场景。
2. 依赖配置
首先,在项目中添加 mockito-core
依赖:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.13.0</version>
<scope>test</scope>
</dependency>
⚠️ 注意:需要使用 5.0.0 或更高版本才能模拟静态方法。
对于旧版 Mockito,需额外添加 mockito-inline
依赖:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
新版 Mockito 已将 mockito-inline
功能集成到 mockito-core
中,无需单独添加。
3. 示例准备
创建一个简单的 Direction
枚举:
public enum Direction {
NORTH,
EAST,
SOUTH,
WEST;
}
再定义一个工具方法,根据传入的枚举值返回描述:
public static String getDescription(Direction direction) {
return switch (direction) {
case NORTH -> "You're headed north.";
case EAST -> "You're headed east.";
case SOUTH -> "You're headed south.";
case WEST -> "You're headed west.";
default -> throw new IllegalArgumentException();
};
}
4. 通过模拟枚举解决问题
测试时通常使用已定义的枚举值,但某些场景需要模拟不存在的枚举值。典型场景包括:
✅ 验证未来新增枚举值不会导致意外行为
✅ 追求更高的代码覆盖率
下面演示如何模拟枚举:
@Test
void givenMockedDirection_whenGetDescription_thenThrowException() {
try (MockedStatic<Direction> directionMock = Mockito.mockStatic(Direction.class)) {
Direction unsupported = Mockito.mock(Direction.class);
Mockito.doReturn(4).when(unsupported).ordinal();
directionMock.when(Direction::values)
.thenReturn(new Direction[] { Direction.NORTH, Direction.EAST, Direction.SOUTH,
Direction.WEST, unsupported });
assertThrows(IllegalArgumentException.class, () -> DirectionUtils.getDescription(unsupported));
}
}
关键步骤:
- 使用
mockStatic()
模拟静态方法调用 - 创建模拟枚举值并指定
ordinal()
返回值 - 修改
values()
方法返回结果,包含模拟值
最终 getDescription()
方法对模拟值抛出 IllegalArgumentException
。
5. 不模拟枚举的替代方案
另一种实现方式是重载枚举定义。在测试目录创建同名枚举并添加新值:
public enum Direction {
NORTH,
EAST,
SOUTH,
WEST,
UNKNOWN;
}
⚠️ 注意:测试枚举必须与源码枚举的完全限定名相同。
测试执行时,JVM 会优先使用测试目录中的枚举定义。测试用例:
@Test
void givenUnknownDirection_whenGetDescription_thenThrowException() {
assertThrows(IllegalArgumentException.class, () -> DirectionUtils.getDescription(Direction.UNKNOWN));
}
❌ 此方案的缺点:
- 测试目录中存在非源码枚举
- 影响整个测试套件的所有用例
6. 总结
本文介绍了使用 Mockito 模拟枚举的两种技术:
- 动态模拟:通过
Mockito.mockStatic()
修改枚举行为 - 静态重载:在测试目录重定义枚举
模拟枚举特别适合验证代码对未来新增枚举值的健壮性。两种方案各有优劣,动态模拟更灵活,静态重载更简单但影响范围大。
完整示例代码见 GitHub 仓库。