如何使用Mockito模拟EntityManager测试getAllSkeletons方法?
用Mockito测试getAllSkeletons方法的分步指南
刚好我之前也处理过类似的JPA仓库方法测试,结合你给出的代码,给你一步步拆解怎么用Mockito完成测试,同时兼顾正常流程和异常场景:
1. 先准备必要的依赖
确保你的项目里引入了Mockito核心库和JUnit(这里以JUnit 5为例),如果是Maven项目,在pom.xml里添加这些依赖:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>4.11.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency>
2. 处理EntityManager的获取问题
你的getEntityManager()方法是调用静态方法EntityController.getEntityManager()获取实例,这里有两种测试方案:
方案A:重构代码(推荐)
更符合依赖注入原则的方式是把EntityManager作为类的依赖注入,这样测试时直接mock即可,不需要处理静态调用:
private final EntityManager entityManager; // 通过构造函数注入EntityManager public YourRepositoryClass(EntityManager entityManager) { this.entityManager = entityManager; } // 简化原有的getEntityManager方法 protected EntityManager getEntityManager() { return entityManager; }
方案B:用Mockito静态mock(不重构的情况)
如果暂时不想调整代码,Mockito 3.4+支持静态方法mock,用来模拟EntityController.getEntityManager()的返回:
try (MockedStatic<EntityController> mockedStatic = Mockito.mockStatic(EntityController.class)) { mockedStatic.when(EntityController::getEntityManager).thenReturn(mockEntityManager); // 在这里执行测试逻辑 }
3. 完整测试代码示例
假设你的类名叫ExamSkeletonRepository,下面是覆盖正常场景和异常场景的测试代码:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class ExamSkeletonRepositoryTest { @Mock private EntityManager entityManager; @Mock private TypedQuery<ExamSkeleton> typedQuery; @InjectMocks private ExamSkeletonRepository repository; // 替换成你的实际类名 @Test void getAllSkeletons_WhenDataExists_ReturnsSkeletonList() { // 1. 准备测试数据 ExamSkeleton skeleton1 = new ExamSkeleton(1L, "exam1.pdf", "Math", true); ExamSkeleton skeleton2 = new ExamSkeleton(2L, "exam2.pdf", "English", false); List<ExamSkeleton> expectedSkeletons = Arrays.asList(skeleton1, skeleton2); // 2. 模拟JPA调用链 when(entityManager.createQuery(anyString(), eq(ExamSkeleton.class))).thenReturn(typedQuery); when(typedQuery.getResultList()).thenReturn(expectedSkeletons); // 如果用方案B,需要包裹静态mock: // try (MockedStatic<EntityController> mockedStatic = mockStatic(EntityController.class)) { // mockedStatic.when(EntityController::getEntityManager).thenReturn(entityManager); // // 执行下面的调用和断言 // } // 3. 调用待测试方法 List<ExamSkeleton> actualSkeletons = repository.getAllSkeletons(); // 4. 验证结果与调用行为 assertEquals(expectedSkeletons.size(), actualSkeletons.size()); assertEquals(expectedSkeletons, actualSkeletons); verify(entityManager, times(1)).createQuery(anyString(), eq(ExamSkeleton.class)); verify(typedQuery, times(1)).getResultList(); } @Test void getAllSkeletons_WhenQueryThrowsException_ReturnsEmptyList() { // 1. 模拟异常抛出 when(entityManager.createQuery(anyString(), eq(ExamSkeleton.class))) .thenThrow(new IllegalArgumentException("Invalid JPA query")); // 如果用方案B,同样需要包裹静态mock // 2. 调用待测试方法 List<ExamSkeleton> result = repository.getAllSkeletons(); // 3. 验证结果 assertTrue(result.isEmpty()); assertEquals(Collections.emptyList(), result); // 若要验证日志输出,需将LOGGER改为类成员变量并@Mock,然后添加: // verify(logger, times(1)).error(any(IllegalArgumentException.class)); } }
额外提示
- 若你的
LOGGER是静态字段,测试日志调用需要用PowerMock或Mockito静态mock;更推荐用SLF4J的@Slf4j注解生成非静态LOGGER,方便直接mock验证。 - 测试要覆盖所有代码分支:正常返回数据、异常返回空列表,确保每个路径都被验证到。
- 优先选择方案A重构代码,不仅简化测试,也符合SOLID设计原则。
内容的提问来源于stack exchange,提问作者Bjorn Pijpops
相关产品推荐
相关产品推荐

