如何用Mockito测试内部调用私有EntityManager查询方法的公共方法
解决方案:Mock EntityManager 而非私有方法
Mockito本身不支持直接Mock私有方法,因为它基于类代理/动态生成子类实现,私有方法无法被代理拦截。但你的私有方法核心依赖EntityManager执行NativeQuery,所以可以通过MockEntityManager来间接控制私有方法的返回结果,从而测试公共方法getUserDetails的逻辑,无需Mock私有方法。
步骤1:准备测试依赖与Mock对象
- 用
@Mock注解MockEntityManager和Query(NativeQuery的父接口) - 用
@InjectMocks注入待测试的目标类实例(假设类名为UserService) - 用
Mockito.when()配置Mock对象的行为,让它返回预设的测试数据
步骤2:编写测试代码示例
假设目标类结构如下:
@Service public class UserService { @PersistenceContext private EntityManager entityManager; public List<UserResponseDto> getUserDetails(Long userId) { List<User> users = getUserByNativeQuery(userId); List<UserDetail> details = getUserDetailsByNativeQuery(userId); return setUserResponseDto(users, details); } private List<User> getUserByNativeQuery(Long userId) { Query query = entityManager.createNativeQuery("SELECT * FROM users WHERE id = ?1", User.class); query.setParameter(1, userId); return query.getResultList(); } private List<UserDetail> getUserDetailsByNativeQuery(Long userId) { Query query = entityManager.createNativeQuery("SELECT * FROM user_details WHERE user_id = ?1", UserDetail.class); query.setParameter(1, userId); return query.getResultList(); } private List<UserResponseDto> setUserResponseDto(List<User> users, List<UserDetail> details) { // 数据转换逻辑 List<UserResponseDto> dtos = new ArrayList<>(); // ... 转换代码 return dtos; } }
对应的JUnit测试代码:
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.Query; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class UserServiceTest { @Mock private EntityManager entityManager; @Mock private Query mockQuery; @InjectMocks private UserService userService; @Test public void getUserDetails_ShouldReturnCorrectDtoList() { // 1. 准备测试数据 Long testUserId = 1L; User testUser = new User(testUserId, "testUser"); UserDetail testDetail = new UserDetail(testUserId, "testAddress"); List<User> mockUsers = List.of(testUser); List<UserDetail> mockDetails = List.of(testDetail); UserResponseDto expectedDto = new UserResponseDto(testUserId, "testUser", "testAddress"); // 2. 配置Mock行为:当调用createNativeQuery时返回预设的Query对象 when(entityManager.createNativeQuery(anyString(), eq(User.class))).thenReturn(mockQuery); when(entityManager.createNativeQuery(anyString(), eq(UserDetail.class))).thenReturn(mockQuery); // 3. 配置Query对象的getResultList返回测试数据 when(mockQuery.setParameter(eq(1), eq(testUserId))).thenReturn(mockQuery); when(mockQuery.getResultList()).thenReturn(mockUsers).thenReturn(mockDetails); // 4. 执行测试方法 List<UserResponseDto> result = userService.getUserDetails(testUserId); // 5. 断言结果符合预期 assertEquals(1, result.size()); assertEquals(expectedDto.getUserId(), result.get(0).getUserId()); assertEquals(expectedDto.getUserName(), result.get(0).getUserName()); assertEquals(expectedDto.getAddress(), result.get(0).getAddress()); } }
关键说明
- 不需要Mock私有方法:通过Mock
EntityManager和Query,让私有方法调用时获取到预设的测试数据,间接实现对私有方法输出的控制 - 重点验证公共方法的逻辑:测试的核心是
getUserDetails如何整合两个私有方法的结果并通过setUserResponseDto转换为返回值,而非私有方法本身的查询逻辑 - 若必须Mock私有方法(不推荐):如果坚持要Mock私有方法,不借助其他工具的话,只能通过反射手动调用私有方法并替换其逻辑,但这种方式侵入性强且维护成本高,不建议使用。
内容的提问来源于stack exchange,提问作者tkz
相关产品推荐
相关产品推荐

