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

Kotlin编写Spring Controller集成测试失败,依赖注入与鉴权参数传递异常

核心问题修复

1. Controller依赖为空的问题

你在@BeforeEach中手动重写已被@Autowired注入的usersService、userController等变量的行为完全错误:你修改的只是测试类中的变量值,Spring容器中管理的Controller、Service实例不会同步更新,自然运行时Controller拿到的是未初始化的旧实例,出现null值。
直接删除@BeforeEach中所有手动给@Autowired变量赋值的代码,所有依赖由Spring容器全权管理,仅保留测试数据的插入逻辑即可。

2. @AuthenticationPrincipal不生效的问题

首先确保测试类开启了权限注解支持:Spring Security 6+添加@EnableMethodSecurity(jsr250Enabled = true),低版本添加@EnableGlobalMethodSecurity(jsr250Enabled = true),保证@RolesAllowed注解可以正常触发。
针对自定义的UserPrincipal类型,可选两种方案注入认证信息:

  • 方案1:请求时直接传入认证信息
    调用mockMvc.perform时添加with(SecurityMockMvcRequestPostProcessors.user(userPrincipal))即可直接注入自定义身份信息,不需要额外注解:
    mockMvc.perform(
        MockMvcRequestBuilders.get("/users/")
            .accept(MediaType.APPLICATION_JSON)
            .with(SecurityMockMvcRequestPostProcessors.user(userPrincipal))
    )
    
  • 方案2:自定义测试注解简化复用
    定义绑定自定义UserPrincipal的测试注解,后续测试直接加注解即可:
    @Target(AnnotationTarget.FUNCTION)
    @Retention(AnnotationRetention.RUNTIME)
    @WithSecurityContext(factory = WithMockUserPrincipalSecurityContextFactory::class)
    annotation class WithMockUserPrincipal(
        val id: String = "1",
        val username: String = "admin",
        val role: String = ROLE_ADMIN
    )
    
    class WithMockUserPrincipalSecurityContextFactory : WithSecurityContextFactory<WithMockUserPrincipal> {
        override fun createSecurityContext(annotation: WithMockUserPrincipal): SecurityContext {
            val user = User(id = UserId(annotation.id), username = annotation.username, role = annotation.role)
            val principal = UserPrincipal(user, null)
            val authentication = UsernamePasswordAuthenticationToken(principal, null, listOf(SimpleGrantedAuthority(annotation.role)))
            return SecurityContextHolder.createEmptyContext().apply {
                this.authentication = authentication
            }
        }
    }
    

3. 测试随机报错(上下文残留)的问题

你已添加的@DirtiesContext注解本身可以实现上下文清理,问题是手动修改Spring容器管理的bean会导致清理逻辑失效,修复第一条的手动赋值问题后即可恢复正常。另外测试类中@MockBean private lateinit var userPrincipal: UserPrincipal属于多余配置,直接删除避免干扰Security上下文注入。

完整测试代码示例

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
@EnableMethodSecurity(jsr250Enabled = true)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
open class UsersControllerTest {

    @Autowired lateinit var mockMvc: MockMvc
    @Autowired lateinit var mapper: ObjectMapper
    @Autowired lateinit var userRepository: UserRepository

    private val validUser = User(UserId("1"), "admin", ROLE_ADMIN)

    @BeforeEach
    fun init(){
        userRepository.insert(validUser)
    }

    @AfterEach
    fun cleanup() {
        userRepository.deleteAll()
        SecurityContextHolder.clearContext()
    }

    @Test
    @WithMockUserPrincipal
    fun `校验管理员查询用户列表逻辑正常`() {
        mockMvc.perform(
            MockMvcRequestBuilders.get("/users/")
                .param("admin", "true")
                .accept(MediaType.APPLICATION_JSON)
                .contentType(MediaType.APPLICATION_JSON)
        ).andExpect(MockMvcResultMatchers.status().isOk)
            .andExpect(MockMvcResultMatchers.jsonPath("$").isArray)
    }

    @Test
    @WithMockUserPrincipal
    fun `校验参数非法时返回400错误`() {
        mockMvc.perform(
            MockMvcRequestBuilders.get("/users/")
                .param("admin", "非法字符串")
                .accept(MediaType.APPLICATION_JSON)
        ).andExpect(MockMvcResultMatchers.status().isBadRequest)
    }
}

内容的提问来源于stack exchange,提问作者Jasper-ui

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 19:57:04