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

Spring Boot集成Keycloak Admin Client单元测试Mock方案咨询

解决Spring Boot + Keycloak Admin Client单元测试数据污染问题

针对你在Spring Boot 2.5.6 + Keycloak 17场景下的单元测试需求,推荐使用Mockito对Keycloak Admin Client进行Mock,完全隔离真实Keycloak服务,避免测试用户污染正式数据。以下是具体实现方案:

1. 先封装Keycloak用户操作服务

首先将Keycloak的CRUD逻辑封装到独立的Service层,解耦客户端依赖,便于后续Mock:

@Service
public class KeycloakUserService {

    private final UsersResource usersResource;

    // 构造注入Keycloak客户端与领域配置
    public KeycloakUserService(Keycloak keycloak, 
                               @Value("${keycloak.realm}") String realm) {
        this.usersResource = keycloak.realm(realm).users();
    }

    // 创建用户
    public UserRepresentation createUser(UserRepresentation user) {
        Response response = usersResource.create(user);
        String userId = CreatedResponseUtil.getCreatedId(response);
        return usersResource.get(userId).toRepresentation();
    }

    // 删除用户
    public void deleteUser(String userId) {
        usersResource.delete(userId);
    }

    // 查询用户
    public UserRepresentation getUserById(String userId) {
        return usersResource.get(userId).toRepresentation();
    }

    // 更新用户
    public UserRepresentation updateUser(String userId, UserRepresentation user) {
        usersResource.get(userId).update(user);
        return usersResource.get(userId).toRepresentation();
    }
}

2. 单元测试中Mock Keycloak依赖

根据测试层级的不同,有两种Mock方式:

方式一:Mock底层Keycloak客户端(测试Service层)

如果需要验证Service层的业务逻辑,可以Mock Keycloak的核心资源类:

@ExtendWith(MockitoExtension.class)
class KeycloakUserServiceTest {

    @Mock
    private Keycloak keycloak;

    @Mock
    private RealmResource realmResource;

    @Mock
    private UsersResource usersResource;

    @Mock
    private UserResource userResource;

    private KeycloakUserService userService;

    @BeforeEach
    void setUp() {
        // 模拟Keycloak调用链
        when(keycloak.realm(anyString())).thenReturn(realmResource);
        when(realmResource.users()).thenReturn(usersResource);
        when(usersResource.get(anyString())).thenReturn(userResource);
        
        userService = new KeycloakUserService(keycloak, "test-realm");
    }

    @Test
    void createUser_ShouldReturnValidUser() {
        // 构造测试用户
        UserRepresentation testUser = new UserRepresentation();
        testUser.setUsername("test-user");
        testUser.setEmail("test@example.com");

        // 模拟创建响应
        Response mockResponse = Mockito.mock(Response.class);
        when(mockResponse.getStatus()).thenReturn(HttpStatus.CREATED.value());
        when(CreatedResponseUtil.getCreatedId(mockResponse)).thenReturn("test-user-id");
        when(usersResource.create(any(UserRepresentation.class))).thenReturn(mockResponse);

        // 模拟返回用户详情
        UserRepresentation returnedUser = new UserRepresentation();
        returnedUser.setId("test-user-id");
        returnedUser.setUsername("test-user");
        when(userResource.toRepresentation()).thenReturn(returnedUser);

        // 执行测试并断言
        UserRepresentation result = userService.createUser(testUser);
        assertNotNull(result);
        assertEquals("test-user-id", result.getId());
        assertEquals("test-user", result.getUsername());
    }
}

方式二:Mock整个Service层(测试Controller层)

如果仅需验证Controller的API逻辑,直接Mock封装好的Service即可:

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private KeycloakUserService userService;

    @Test
    void createUser_ShouldReturnCreatedStatus() throws Exception {
        UserRepresentation testUser = new UserRepresentation();
        testUser.setUsername("test-user");
        testUser.setEmail("test@example.com");

        UserRepresentation createdUser = new UserRepresentation();
        createdUser.setId("test-id");
        createdUser.setUsername("test-user");

        when(userService.createUser(any(UserRepresentation.class))).thenReturn(createdUser);

        mockMvc.perform(post("/api/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(new ObjectMapper().writeValueAsString(testUser)))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.id").value("test-id"))
                .andExpect(jsonPath("$.username").value("test-user"));
    }
}

3. 额外注意事项

  • 测试环境配置:在src/test/resources/application-test.yml中可以配置无效的Keycloak地址,确保即使Mock失效也不会连接正式环境。
  • 集成测试备选:如果需要真实Keycloak环境但不想污染数据,可以使用Keycloak测试领域(测试后清理数据),或用TestContainers启动临时Keycloak容器,测试结束后销毁容器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:18:30