【问题标题】:Why is spring Boot 2.4.5 with Junit5 and mocked beans is throwing StackOverflowErrors?为什么带有 Junit 5 和模拟 bean 的 spring Boot 2.4.5 会抛出 StackOverflowError?
【发布时间】:2021-05-15 12:36:28
【问题描述】:

上下文

  1. 将 gradle 从 5.5.1 升级到 7.0.2
  2. 经过测试 - 一切正常
  3. 将 spring 从 2.1.5 升级到 2.4.1
  4. 将测试注释从 junit 4 迁移到 junit 5

结果

  1. 单元测试工作
  2. 集成测试不要工作

问题

  1. 在应用程序上下文中重置 Mocks 时启动集成测试失败 @ResetMocksTestExecutionListneer.resetMocks
  2. 有问题的 bean 的名称为 beanifyAuthenticationManager,其代理为 interface org.springframework.security.authentication.AuthenticationManager
    1. 检测此 bean 是否为 mock 会陷入无限循环。
    2. toString 方法也是如此。

问题

  1. 我做错了吗?
  2. 这是一个应该报告给 Spring 的问题吗?

其他信息

  1. 由定义为的自定义注释触发的集成测试
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@SpringBootTest(
  classes = {PmpTestApplication.class},
  webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
@ActiveProfiles(profiles = {"test"})
@TestExecutionListeners(value = {WithDomainAuthorizationExecutionListener.class},
  mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
@TestPropertySource(properties = {
  "spring.cloud.consul.enabled=false"})
public @interface AppIntTest {
  1. 集成测试看起来像
@AppIntTest
@ExtendWith(SpringExtension.class)
public class SomeRestBusinessControllerIntTest extends AbstractControllerIntTest {
  1. 实际的StackOverflowError 异常是从
  2. 引发的
AopTestUtils.getUltimateTargetObject
[...]
    try {
            if (AopUtils.isAopProxy(candidate) && candidate instanceof Advised) {
                Object target = ((Advised) candidate).getTargetSource().getTarget();
                if (target != null) {
                    return (T) getUltimateTargetObject(target);
                }
            }
        }
        catch (Throwable ex) {
            throw new IllegalStateException("Failed to unwrap proxied object", ex);
        }
[...]

很容易看出它是如何在 StackOverflowError 中运行的。 在再次递归调用 getUltimateTargetObject 之前需要检查 target <> candidate

【问题讨论】:

  • 理论上,建议的target <> candidate 检查应该是不必要的,我想看看为什么/如何在您的场景中发生这种情况。因此,请在github.com/spring-projects/spring-framework/issues/new 上开票,并使用一个最小项目来复制StackOverflowError,我们(Spring 团队)将对其进行调查。
  • 您可以尝试使用静态方法将AuthenticationManager bean 定义外部化到单独的配置类中吗?这通常有助于 bean 循环。

标签: spring mockito integration-testing junit5


【解决方案1】:

虽然我没有明确解释为什么会发生这种情况,但当我从安全配置中删除显式 bean 声明时,问题就停止了

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
    securedEnabled = true,
    prePostEnabled = true)
@Import(SecurityProblemSupport.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
[...]
    // this explicit bean declaration was removed
    @Bean
    public AuthenticationManager beanifyAuthenticationManager() throws Exception {
        // will use #configure to build the bean
        return authenticationManager();
    }
    

我的猜测是,过去的 Spring 版本需要 AuthenticationManager 的这种显式 bean 声明来触发某些安全配置机制。

【讨论】:

  • 你能把抛出的确切错误也发布一下吗?
  • @Boug : 添加了额外的异常细节 + 我认为应该如何修复的 2 美分
  • 您可能需要在 Github 中发布一个最小复制示例项目,并在春季开票。您所描述的是异常是从 spring it self 引发的。那肯定是spring来解决的。不将AuthenticationManager 注册为 bean 不是一个可行的解决方案。这可能正是他们需要检查的原因
猜你喜欢
  • 2020-02-24
  • 1970-01-01
  • 2021-08-02
  • 1970-01-01
  • 1970-01-01
  • 2022-12-21
  • 2018-03-09
  • 2019-07-01
  • 2019-03-25
相关资源
最近更新 更多