27.2. 构建应用程序
良好的应用程序包括测试覆盖。这个示例测试了 Timetable Red Hat Business Optimizer Spring Boot 应用程序。它使用 JUnit 测试来生成测试数据集,并将其发送到 TimeTableController
来解决。
流程
使用以下内容创建 src/test/java/com/example/solver/TimeTableControllerTest.java
类:
package com.example.solver; import java.time.DayOfWeek; import java.time.LocalTime; import java.util.ArrayList; import java.util.List; import com.example.domain.Lesson; import com.example.domain.Room; import com.example.domain.TimeTable; import com.example.domain.Timeslot; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @SpringBootTest(properties = { "optaplanner.solver.termination.spent-limit=1h", // Effectively disable this termination in favor of the best-score-limit "optaplanner.solver.termination.best-score-limit=0hard/*soft"}) public class TimeTableControllerTest { @Autowired private TimeTableController timeTableController; @Test @Timeout(600_000) public void solve() { TimeTable problem = generateProblem(); TimeTable solution = timeTableController.solve(problem); assertFalse(solution.getLessonList().isEmpty()); for (Lesson lesson : solution.getLessonList()) { assertNotNull(lesson.getTimeslot()); assertNotNull(lesson.getRoom()); } assertTrue(solution.getScore().isFeasible()); } private TimeTable generateProblem() { List<Timeslot> timeslotList = new ArrayList<>(); timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(8, 30), LocalTime.of(9, 30))); timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(9, 30), LocalTime.of(10, 30))); timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(10, 30), LocalTime.of(11, 30))); timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(13, 30), LocalTime.of(14, 30))); timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(14, 30), LocalTime.of(15, 30))); List<Room> roomList = new ArrayList<>(); roomList.add(new Room("Room A")); roomList.add(new Room("Room B")); roomList.add(new Room("Room C")); List<Lesson> lessonList = new ArrayList<>(); lessonList.add(new Lesson(101L, "Math", "B. May", "9th grade")); lessonList.add(new Lesson(102L, "Physics", "M. Curie", "9th grade")); lessonList.add(new Lesson(103L, "Geography", "M. Polo", "9th grade")); lessonList.add(new Lesson(104L, "English", "I. Jones", "9th grade")); lessonList.add(new Lesson(105L, "Spanish", "P. Cruz", "9th grade")); lessonList.add(new Lesson(201L, "Math", "B. May", "10th grade")); lessonList.add(new Lesson(202L, "Chemistry", "M. Curie", "10th grade")); lessonList.add(new Lesson(203L, "History", "I. Jones", "10th grade")); lessonList.add(new Lesson(204L, "English", "P. Cruz", "10th grade")); lessonList.add(new Lesson(205L, "French", "M. Curie", "10th grade")); return new TimeTable(timeslotList, roomList, lessonList); } }
package com.example.solver;
import java.time.DayOfWeek;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import com.example.domain.Lesson;
import com.example.domain.Room;
import com.example.domain.TimeTable;
import com.example.domain.Timeslot;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@SpringBootTest(properties = {
"optaplanner.solver.termination.spent-limit=1h", // Effectively disable this termination in favor of the best-score-limit
"optaplanner.solver.termination.best-score-limit=0hard/*soft"})
public class TimeTableControllerTest {
@Autowired
private TimeTableController timeTableController;
@Test
@Timeout(600_000)
public void solve() {
TimeTable problem = generateProblem();
TimeTable solution = timeTableController.solve(problem);
assertFalse(solution.getLessonList().isEmpty());
for (Lesson lesson : solution.getLessonList()) {
assertNotNull(lesson.getTimeslot());
assertNotNull(lesson.getRoom());
}
assertTrue(solution.getScore().isFeasible());
}
private TimeTable generateProblem() {
List<Timeslot> timeslotList = new ArrayList<>();
timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(8, 30), LocalTime.of(9, 30)));
timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(9, 30), LocalTime.of(10, 30)));
timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(10, 30), LocalTime.of(11, 30)));
timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(13, 30), LocalTime.of(14, 30)));
timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(14, 30), LocalTime.of(15, 30)));
List<Room> roomList = new ArrayList<>();
roomList.add(new Room("Room A"));
roomList.add(new Room("Room B"));
roomList.add(new Room("Room C"));
List<Lesson> lessonList = new ArrayList<>();
lessonList.add(new Lesson(101L, "Math", "B. May", "9th grade"));
lessonList.add(new Lesson(102L, "Physics", "M. Curie", "9th grade"));
lessonList.add(new Lesson(103L, "Geography", "M. Polo", "9th grade"));
lessonList.add(new Lesson(104L, "English", "I. Jones", "9th grade"));
lessonList.add(new Lesson(105L, "Spanish", "P. Cruz", "9th grade"));
lessonList.add(new Lesson(201L, "Math", "B. May", "10th grade"));
lessonList.add(new Lesson(202L, "Chemistry", "M. Curie", "10th grade"));
lessonList.add(new Lesson(203L, "History", "I. Jones", "10th grade"));
lessonList.add(new Lesson(204L, "English", "P. Cruz", "10th grade"));
lessonList.add(new Lesson(205L, "French", "M. Curie", "10th grade"));
return new TimeTable(timeslotList, roomList, lessonList);
}
}
此测试会验证之后,所有较少记录都会被分配给一个时间插槽和房间。它还会验证它发现了一个可行的解决方案(没有硬约束中断)。
通常,解析器会在 200 毫秒内找到可行的解决方案。请注意,@SpringBootTest
注释 的属性
如何覆盖 solver 终止,以便在找到可行的解决方案(0hard soft
)时立即终止。这可避免硬编码一个固定时间,因为单元测试可能会在任意硬件上运行。这种方法可确保测试运行足够长,以找到可行的解决方案,即使在较慢的机器上也是如此。但是,即使在快速机器上,它不会运行毫秒的时间超过它。