Timefold:如何对Variable Listener进行单元测试?
OptaPlanner VariableListener单元测试实现方案
一、这类单元测试是否必要?
非常有必要。自定义VariableListener的逻辑(比如跳过周末的时间计算)是任务调度规则的核心,直接决定任务的开始时间是否符合业务要求。单元测试可以:
- 快速验证边界场景(如周五、周六、周日的时间处理)
- 避免集成测试阶段才发现逻辑错误
- 为后续逻辑修改提供回归测试保障
二、具体实现步骤
1. 准备依赖
确保项目中引入OptaPlanner测试依赖(以Maven为例):
<dependency> <groupId>org.optaplanner</groupId> <artifactId>optaplanner-test</artifactId> <version>${optaplanner.version}</version> <scope>test</scope> </dependency>
同时引入JUnit 5作为测试框架。
2. 实例化核心测试对象
(1)创建规划实体与测试数据
先构造你的规划实体(例如FoodPackingTask)和对应的解决方案(例如FoodPackingSchedule),模拟真实业务场景的数据:
// 模拟前序任务 FoodPackingTask previousTask = new FoodPackingTask(); // 模拟当前任务,关联前序任务 FoodPackingTask currentTask = new FoodPackingTask(); currentTask.setPreviousTask(previousTask); // 组装解决方案 FoodPackingSchedule solution = new FoodPackingSchedule(); solution.getTaskList().addAll(List.of(previousTask, currentTask));
(2)初始化ScoreDirector
通过OptaPlanner的TestScoreDirectorFactory创建ScoreDirector,它是触发VariableListener的核心上下文:
// 加载你的Solver配置文件 SolverFactory<FoodPackingSchedule> solverFactory = SolverFactory.createFromXmlResource("food-packing-solver-config.xml"); // 创建测试用的ScoreDirector工厂 TestScoreDirectorFactory<FoodPackingSchedule> testScoreDirectorFactory = TestScoreDirectorFactory.create(solverFactory); // 构建ScoreDirector实例 ScoreDirector<FoodPackingSchedule> scoreDirector = testScoreDirectorFactory.buildScoreDirector(); // 将解决方案设置到ScoreDirector中 scoreDirector.setWorkingSolution(solution);
(3)实例化自定义VariableListener
直接创建你的监听器实例即可:
YourStartTimeVariableListener startTimeListener = new YourStartTimeVariableListener();
3. 触发监听器并验证逻辑
通过ScoreDirector的变量变更方法模拟真实场景下的监听器触发,然后验证结果是否符合预期:
示例:测试周五结束的前序任务
// 设置前序任务结束时间为周五17:00 LocalDateTime fridayEndTime = LocalDateTime.of(2024, 5, 17, 17, 0); scoreDirector.beforeVariableChanged(previousTask, "endTime"); previousTask.setEndTime(fridayEndTime); scoreDirector.afterVariableChanged(previousTask, "endTime"); // 验证当前任务的开始时间是否跳至下周一9:00(假设工作时间为周一至周五9:00-17:00) LocalDateTime expectedStartTime = LocalDateTime.of(2024, 5, 20, 9, 0); assertEquals(expectedStartTime, currentTask.getStartTime());
其他边界场景测试
- 前序任务结束时间为周六:验证开始时间为下周一9:00
- 前序任务结束时间为周四16:00:验证开始时间为周五9:00
- 前序任务结束时间为周日23:00:验证开始时间为下周一9:00
4. 简化测试(可选)
如果监听器逻辑不依赖复杂的ScoreDirector上下文,也可以直接调用监听器的方法:
// 手动触发监听器 startTimeListener.afterVariableChanged(scoreDirector, currentTask, "previousTask"); // 验证结果 assertEquals(expectedStartTime, currentTask.getStartTime());
三、注意事项
- 确保Solver配置文件中已正确注册你的VariableListener,否则TestScoreDirectorFactory无法识别
- 测试用例要覆盖所有业务逻辑的边界场景,避免遗漏特殊情况
- 如果监听器依赖其他变量或实体关系,测试时要完整模拟这些依赖
内容的提问来源于stack exchange,提问作者user23511321
相关产品推荐
相关产品推荐

