You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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私有方法:通过MockEntityManager和Query,让私有方法调用时获取到预设的测试数据,间接实现对私有方法输出的控制
  • 重点验证公共方法的逻辑:测试的核心是getUserDetails如何整合两个私有方法的结果并通过setUserResponseDto转换为返回值,而非私有方法本身的查询逻辑
  • 若必须Mock私有方法(不推荐):如果坚持要Mock私有方法,不借助其他工具的话,只能通过反射手动调用私有方法并替换其逻辑,但这种方式侵入性强且维护成本高,不建议使用。

内容的提问来源于stack exchange,提问作者tkz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 14:53:08