test: 新增 StudySessionsServiceImpl 单元测试 (10 cases)

覆盖场景:ONGOING/PAUSED 状态的消息返回、报告创建、
边界值、会话不存在异常、updateById 调用验证
This commit is contained in:
2026-05-30 17:37:29 +08:00
parent d7e1d9a10c
commit d0c78ceb28
@@ -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));
}
/**
* 场景1ONGOING 状态,有效时间 < 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));
}
/**
* 场景2ONGOING 状态,有效时间 > 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());
}
/**
* 场景3PAUSED 状态,有效时间 < 10分钟 → 返回 nullPAUSED 不触发提示)
*/
@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 仍应被实体层清零");
}
/**
* 场景4PAUSED 状态,有效时间 > 10分钟 → 返回 nulleffectiveTime 保留
*/
@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);
}
}