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

从Spring Boot 3.1.5迁移至3.2.0后单元测试失败求助

问题背景

我们近期将Spring Boot父依赖从3.1.5版本迁移至3.2.0版本:

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.2.0</version>
  <relativePath />
</parent>

迁移后单元测试失败,测试报告报错:

[ERROR] nl.gateway.WebControllerTest.indexRNB -- Time elapsed: 0.012 s <<< FAILURE!
java.lang.AssertionError: Range for response status value 500 INTERNAL_SERVER_ERROR expected:<SUCCESSFUL> but was:<SERVER_ERROR>
    at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59)
    at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:122)
    at org.springframework.test.web.reactive.server.StatusAssertions.lambda$assertSeriesAndReturn$5(StatusAssertions.java:244)
    at org.springframework.test.web.reactive.server.ExchangeResult.assertWithDiagnostics(ExchangeResult.java:222)
    at org.springframework.test.web.reactive.server.StatusAssertions.assertSeriesAndReturn(StatusAssertions.java:243)
    at org.springframework.test.web.reactive.server.StatusAssertions.is2xxSuccessful(StatusAssertions.java:187)
    at nl.gateway.WebControllerTest.indexRNB(WebControllerTest.java:100)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

IDE中捕获的详细错误栈:

java.lang.IllegalStateException: No primary or single unique constructor found for interface org.springframework.security.oauth2.core.oidc.user.OidcUser
    at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:267) ~[spring-beans-6.1.1.jar:6.1.1]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    *__checkpoint ⇢ org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers$MutatorFilter [DefaultWebFilterChain]
    *__checkpoint ⇢ org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers$OAuth2ClientMutator$$Lambda$1094/0x00000008011c7c38 [DefaultWebFilterChain]
    *__checkpoint ⇢ org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers$SetupMutatorFilter [DefaultWebFilterChain]
    *__checkpoint ⇢ HTTP GET "/" [ExceptionHandlingWebHandler]
Original Stack Trace:

相关代码片段:
单元测试代码:

@Test
void indexRNB() {
    client
            .mutateWith(mockOidcLogin().idToken(id -> id
                    .subject("Subject A")
                    .claim("groups", List.of("ORG-RNB"))))
            .get().uri("/").exchange()
            .expectStatus().is2xxSuccessful()
            .expectBody().consumeWith(response -> assertThat(new String(response.getResponseBody()))
                    .contains(">Logout Subject A"));
}

控制器代码:

@GetMapping(value = "/")
public String getIndex(
        @AuthenticationPrincipal(errorOnInvalidType = true) OidcUser user,
        @RequestParam MultiValueMap<String, String> params,
        Model model,
        @Value("${aggregatedvolumes.suppliers}") List<String> aggregatedvolumesSuppliers) {
    // Conditionally show links
    return "root";
}

尝试过降级多个依赖库但未解决问题,想问Spring Boot 3.1.5与3.2.0之间的哪些变更会引发这个问题?

问题分析及解决方法

核心变更原因

Spring Boot 3.2.0同步升级了Spring Security到6.1.x版本,该版本对OIDC测试模拟逻辑做了调整:mockOidcLogin()默认生成的认证主体不再是OidcUser实例,而是其父类AbstractOAuth2User(属于OAuth2User类型)。但你的控制器方法通过@AuthenticationPrincipal(errorOnInvalidType = true)强制绑定OidcUser接口,导致类型转换失败,抛出IllegalStateException,最终返回500服务器错误。

具体修复方案

在单元测试中,显式配置mockOidcLogin()生成OidcUser实例,通过user()方法指定构建逻辑:

@Test
void indexRNB() {
    client
            .mutateWith(mockOidcLogin()
                    .idToken(id -> id
                            .subject("Subject A")
                            .claim("groups", List.of("ORG-RNB")))
                    .user(oidcUser -> oidcUser
                            .authorities(List.of(new SimpleGrantedAuthority("ROLE_USER"))))) // 根据业务需求添加权限
            .get().uri("/").exchange()
            .expectStatus().is2xxSuccessful()
            .expectBody().consumeWith(response -> assertThat(new String(response.getResponseBody()))
                    .contains(">Logout Subject A"));
}

如果业务逻辑允许,也可以将控制器中的参数类型从OidcUser改为OAuth2User,这样无需修改测试代码,但需要评估是否会影响其他业务逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 21:47:02