HMCL单元测试模拟框架:Mockito实战与依赖注入
单元测试现状与痛点
HMCL项目采用分层架构设计,核心功能模块如游戏版本管理HMCLGameRepository.java、依赖下载DefaultDependencyManager.java等存在复杂的外部依赖关系。传统测试方法面临三大挑战:第三方服务依赖(如Minecraft服务器API)、硬件资源限制(如显卡驱动测试)、测试环境一致性难以保证。通过分析项目测试目录结构,发现现有测试主要集中在工具类如AggregatedObservableListTest.java,业务逻辑层测试覆盖率有待提升。
Mockito框架集成方案
环境配置
HMCL使用Gradle构建系统,在gradle/libs.versions.toml中添加Mockito依赖:
[versions]
mockito = "4.11.0"
[libraries]
mockito-core = { group = "org.mockito", name = "mockito-core", version.ref = "mockito" }
mockito-junit-jupiter = { group = "org.mockito", name = "mockito-junit-jupiter", version.ref = "mockito" }
核心注解应用
| 注解 | 作用 | 应用场景 |
|---|---|---|
@Mock | 创建模拟对象 | 替代GameRepository等外部依赖 |
@InjectMocks | 自动注入依赖 | 测试HMCLGameLauncher等服务类 |
@Captor | 参数捕获器 | 验证LaunchOptions参数传递 |
@Spy | 部分模拟真实对象 | 测试DownloadProvider的特定方法 |
实战案例:游戏启动器测试
测试场景设计
以游戏启动流程为例,需要模拟以下依赖:
- 账户认证服务
OAuthAccount.java - 版本元数据加载
Version.java - 日志系统
Log.java
测试代码实现
@ExtendWith(MockitoExtension.class)
class HMCLGameLauncherTest {
@Mock
private OAuthAccount mockAccount;
@Mock
private Version mockVersion;
@InjectMocks
private HMCLGameLauncher launcher;
@Test
void testLaunchSuccess() {
// Arrange
when(mockAccount.getUsername()).thenReturn("TestUser");
when(mockVersion.getId()).thenReturn("1.19.3");
when(mockVersion.getMainClass()).thenReturn("net.minecraft.client.main.Main");
// Act
boolean result = launcher.launch(mockVersion, mockAccount);
// Assert
assertTrue(result);
verify(mockAccount).getAuthInfo();
verify(mockVersion, times(2)).getId();
verify(Log.class);
}
}
依赖注入最佳实践
构造函数注入
在HMCLGameRepository.java中采用构造函数注入:
public class HMCLGameRepository implements GameRepository {
private final CacheRepository cacheRepo;
// 便于测试的构造函数
public HMCLGameRepository(CacheRepository cacheRepo) {
this.cacheRepo = cacheRepo;
}
}
测试替身策略
针对不同依赖类型选择合适的测试替身:
- 模拟对象(Mock): 用于
AuthenticationService等外部服务 - 桩对象(Stub): 用于返回固定版本列表的
VersionList - 假对象(Fake): 使用内存实现的
InMemoryCacheRepository
测试覆盖率提升
通过JaCoCo插件生成测试覆盖率报告,重点优化以下模块:
- 游戏版本解析
Version.java - 依赖下载管理器
DefaultDependencyManager.java - 账户认证流程
microsoft/
常见问题解决方案
静态方法模拟
使用PowerMock扩展处理静态日志调用:
@PrepareForTest(Log.class)
class LoggingTest {
@Test
void testErrorLogging() {
PowerMockito.mockStatic(Log.class);
// 执行测试代码
PowerMockito.verifyStatic(Log.class);
Log.error(anyString(), any(Exception.class));
}
}
私有方法测试
通过反射工具类ReflectionUtils.java访问私有方法:
@Test
void testPrivateMethod() throws Exception {
// 调用私有方法
Method method = HMCLGameLauncher.class.getDeclaredMethod("validateOptions", LaunchOptions.class);
method.setAccessible(true);
boolean result = (boolean) method.invoke(launcher, mockOptions);
assertTrue(result);
}
测试框架演进路线
HMCL测试架构正从传统单元测试向行为驱动开发(BDD)转型,计划引入Cucumber框架实现以下改进:
- 业务场景与测试用例分离
- 多模块集成测试自动化
- 测试数据参数化管理
通过持续集成流水线Jenkinsfile配置,已实现测试覆盖率门禁检查,要求核心模块测试覆盖率不低于75%。未来将进一步完善测试替身策略,重点提升game/和download/模块的测试质量。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




