test: 新增 StudySessionsServiceImpl 单元测试 (10 cases)
覆盖场景:ONGOING/PAUSED 状态的消息返回、报告创建、 边界值、会话不存在异常、updateById 调用验证
This commit is contained in:
+213
@@ -0,0 +1,213 @@
|
|||||||
|
package com.guo.learningprogresstracker.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
|
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
|
||||||
|
import com.guo.learningprogresstracker.entity.StudySessionsEntity;
|
||||||
|
import com.guo.learningprogresstracker.enums.StudySessionStateEnum;
|
||||||
|
import com.guo.learningprogresstracker.mapper.StudyReportFragmentsMapper;
|
||||||
|
import com.guo.learningprogresstracker.mapper.StudyReportsMapper;
|
||||||
|
import com.guo.learningprogresstracker.mapper.StudySessionsMapper;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.jupiter.MockitoSettings;
|
||||||
|
import org.mockito.quality.Strictness;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||||
|
class StudySessionsServiceImplTest {
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
@InjectMocks
|
||||||
|
private StudySessionsServiceImpl studySessionsService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private StudyReportsMapper studyReportsMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private StudyReportFragmentsMapper studyReportFragmentsMapper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private StudySessionsMapper studySessionsMapper;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
// stub updateById,避免调用真实的 getBaseMapper()
|
||||||
|
doReturn(true).when(studySessionsService).updateById(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
private StudySessionsEntity createSession(String state, int minutesAgo) {
|
||||||
|
StudySessionsEntity session = new StudySessionsEntity();
|
||||||
|
session.setSessionNum("SESSION_TEST");
|
||||||
|
session.setSessionState(state);
|
||||||
|
session.setStartTime(LocalDateTime.now().minusMinutes(minutesAgo));
|
||||||
|
session.setLastStartTime(LocalDateTime.now().minusMinutes(minutesAgo));
|
||||||
|
session.setEffectiveTime(0);
|
||||||
|
session.setActualTime(0);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mockGetOneOpt(StudySessionsEntity session) {
|
||||||
|
doReturn(Optional.of(session)).when(studySessionsService).getOneOpt(any(Wrapper.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景1:ONGOING 状态,有效时间 < 10分钟 → 返回提示消息
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void endedStudySession_ongoing_lessThan10Min_shouldReturnMessage() throws Exception {
|
||||||
|
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 5);
|
||||||
|
mockGetOneOpt(session);
|
||||||
|
|
||||||
|
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
|
||||||
|
|
||||||
|
assertEquals("本次有效学习时间不足10分钟,不计入总学习时间", message);
|
||||||
|
assertEquals(0, session.getEffectiveTime(), "effectiveTime 应被清零");
|
||||||
|
assertEquals(StudySessionStateEnum.ENDED.name(), session.getSessionState());
|
||||||
|
verify(studyReportsMapper).insert(any(StudyReportsEntity.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景2:ONGOING 状态,有效时间 > 10分钟 → 返回 null(正常结束)
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void endedStudySession_ongoing_moreThan10Min_shouldReturnNull() throws Exception {
|
||||||
|
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 15);
|
||||||
|
mockGetOneOpt(session);
|
||||||
|
|
||||||
|
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
|
||||||
|
|
||||||
|
assertNull(message, "有效时间超过10分钟不应返回提示");
|
||||||
|
assertTrue(session.getEffectiveTime() > 0, "effectiveTime 应保留");
|
||||||
|
assertEquals(StudySessionStateEnum.ENDED.name(), session.getSessionState());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景3:PAUSED 状态,有效时间 < 10分钟 → 返回 null(PAUSED 不触发提示)
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void endedStudySession_paused_lessThan10Min_shouldReturnNull() throws Exception {
|
||||||
|
StudySessionsEntity session = createSession(StudySessionStateEnum.PAUSED.name(), 5);
|
||||||
|
session.setEffectiveTime(5 * 60);
|
||||||
|
mockGetOneOpt(session);
|
||||||
|
|
||||||
|
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
|
||||||
|
|
||||||
|
assertNull(message, "PAUSED 状态结束不返回提示(用户已主动暂停)");
|
||||||
|
assertEquals(0, session.getEffectiveTime(), "effectiveTime 仍应被实体层清零");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景4:PAUSED 状态,有效时间 > 10分钟 → 返回 null,effectiveTime 保留
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void endedStudySession_paused_moreThan10Min_shouldReturnNull() throws Exception {
|
||||||
|
StudySessionsEntity session = createSession(StudySessionStateEnum.PAUSED.name(), 15);
|
||||||
|
session.setEffectiveTime(15 * 60);
|
||||||
|
mockGetOneOpt(session);
|
||||||
|
|
||||||
|
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
|
||||||
|
|
||||||
|
assertNull(message);
|
||||||
|
assertTrue(session.getEffectiveTime() > 0, "effectiveTime 应保留");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景5:无论是否触发清零,学习报告都应正常创建
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void endedStudySession_shouldAlwaysCreateReport() throws Exception {
|
||||||
|
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 3);
|
||||||
|
mockGetOneOpt(session);
|
||||||
|
|
||||||
|
studySessionsService.endedStudySession("SESSION_TEST", "测试报告内容");
|
||||||
|
|
||||||
|
verify(studyReportsMapper).insert(argThat(report ->
|
||||||
|
"SESSION_TEST".equals(((StudyReportsEntity) report).getSessionNum())
|
||||||
|
&& "测试报告内容".equals(((StudyReportsEntity) report).getContent())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景6:ONGOING 状态,有效时间恰好 = 10分钟 → 有效时间保留(600 不 < 600)
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void endedStudySession_ongoing_exactly10Min_shouldReturnNull() throws Exception {
|
||||||
|
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 10);
|
||||||
|
mockGetOneOpt(session);
|
||||||
|
|
||||||
|
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
|
||||||
|
|
||||||
|
assertNull(message, "有效时间等于10分钟应保留(阈值为 < 600)");
|
||||||
|
assertTrue(session.getEffectiveTime() >= 10 * 60, "effectiveTime 应保留");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景7:会话不存在 → 抛出异常
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void endedStudySession_sessionNotFound_shouldThrowException() {
|
||||||
|
doReturn(Optional.empty()).when(studySessionsService).getOneOpt(any(Wrapper.class));
|
||||||
|
|
||||||
|
assertThrows(Exception.class, () ->
|
||||||
|
studySessionsService.endedStudySession("NOT_EXIST", "内容"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景8:ONGOING 状态,有效时间极短(1分钟) → 返回提示消息
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void endedStudySession_ongoing_veryShortTime_shouldReturnMessage() throws Exception {
|
||||||
|
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 1);
|
||||||
|
mockGetOneOpt(session);
|
||||||
|
|
||||||
|
String message = studySessionsService.endedStudySession("SESSION_TEST", "学习内容");
|
||||||
|
|
||||||
|
assertEquals("本次有效学习时间不足10分钟,不计入总学习时间", message);
|
||||||
|
assertEquals(0, session.getEffectiveTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景9:ONGOING 状态,刚开始就结束(effectiveTime ≈ 0) → 返回提示消息
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void endedStudySession_ongoing_immediatelyEnded_shouldReturnMessage() throws Exception {
|
||||||
|
StudySessionsEntity session = new StudySessionsEntity();
|
||||||
|
session.setSessionNum("SESSION_INSTANT");
|
||||||
|
session.setSessionState(StudySessionStateEnum.ONGOING.name());
|
||||||
|
session.setStartTime(LocalDateTime.now());
|
||||||
|
session.setLastStartTime(LocalDateTime.now());
|
||||||
|
session.setEffectiveTime(0);
|
||||||
|
session.setActualTime(0);
|
||||||
|
mockGetOneOpt(session);
|
||||||
|
|
||||||
|
String message = studySessionsService.endedStudySession("SESSION_INSTANT", "内容");
|
||||||
|
|
||||||
|
assertEquals("本次有效学习时间不足10分钟,不计入总学习时间", message);
|
||||||
|
assertEquals(0, session.getEffectiveTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场景10:确认 updateById 被调用(会话状态持久化)
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void endedStudySession_shouldCallUpdateById() throws Exception {
|
||||||
|
StudySessionsEntity session = createSession(StudySessionStateEnum.ONGOING.name(), 5);
|
||||||
|
mockGetOneOpt(session);
|
||||||
|
|
||||||
|
studySessionsService.endedStudySession("SESSION_TEST", "内容");
|
||||||
|
|
||||||
|
verify(studySessionsService).updateById(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user