【问题标题】:Spring GenericWebApplicationContext gets loaded despite web-application-type: NONE尽管 web-application-type: NONE 仍会加载 Spring GenericWebApplicationContext
【发布时间】:2021-12-24 22:21:55
【问题描述】:

在我的 Spring 2.5 Web 应用程序中,我正在尝试编写一个测试,在该测试中我禁用它是一个 Web 应用程序的事实:这是因为该应用程序还包含一些计划任务,这些任务使用一些 Oauth2 组件来调用其他组件服务。

这些 Oauth2 组件的行为会有所不同,具体取决于它们是否从 Servlet 上下文触发(请参阅 official doc)。

现在,当我执行 @SpringBootTest 时,我看到了:

[org.springframework.context.support.AbstractApplicationContext] [main] [629] [DEBUG] Refreshing org.springframework.web.context.support.GenericWebApplicationContext        

我想禁用它,所以在我的 application.yml 中,我设置了 spring.main.web-application-type=NONE ,但它不起作用我仍然看到 WebApplicationContext (我还将属性设置为错误的值,以确保它是考虑和失败:确实如此,所以我知道我的 applicaion.yml 文件已被我读取并被我测试考虑)。

我希望一旦我能够禁用它,然后ServletTestExecutionListener 不会被触发,也不会创建MockServletContext

【问题讨论】:

    标签: spring-boot spring-boot-test


    【解决方案1】:

    因为我们无法轻易启用我们需要的自动配置,当我们设置 webEnvironment = SpringBootTest.WebEnvironment.NONE(请参阅https://stackoverflow.com/a/69970013/3067542 中的 cmets)时,我们需要使用不同的方法。

    我们不能轻易阻止ServletTestExecutionListener 被调用,但是我们可以添加我们自己的TestExecutionListener 来重置我们在测试上下文中需要的部分。幸运的是,我们添加的监听器在 ServletTestExecutionListener 之后被调用,所以确实可以“撤消”我们需要的东西。

    import org.springframework.test.context.TestContext;
    import org.springframework.test.context.support.AbstractTestExecutionListener;
    import org.springframework.web.context.request.RequestContextHolder;
    
    /**
     * It's difficult to test a scheduled task that is part of a web application : out of the box, Spring Boot will set up a mock servlet context
     * through {@link org.springframework.test.context.web.ServletTestExecutionListener}. But in production, the servlet context is null and HTTP calls through {@code WebClient} may fail
     * if it's not configured correctly - see <a href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/oauth2/client/AuthorizedClientServiceOAuth2AuthorizedClientManager.html">the doc</a>
     * for an example of how it should typically configured.
     * If not configured correctly, WebClient calls may fail at runtime, and we may not be able to reproduce easily in a test, because servletRequest is no tnull like in prod, but set to a mock servlet context.
     *
     * Using {@code ResettingServletContextToNullListener}, we reset to null the servletContext that was initialized in {@link org.springframework.test.context.web.ServletTestExecutionListener}, and create a context that is much closer to what it is at runtime in production
     */
    public class ResettingServletContextToNullListener extends AbstractTestExecutionListener {
    
      @Override
      public void beforeTestMethod(TestContext testContext) {
        System.out.println("resetting servletContext to null, like it is at runtime when a scheduled task is triggered..");
        RequestContextHolder.setRequestAttributes(null);
      }
    
    }
    

    然后我们可以简单地将其添加到我们的测试中:

    @SpringBootTest
    @TestExecutionListeners(value = ResettingServletContextToNullListener.class, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
    class MyScheduledTaskIntegrationTest {
    
    ...
    
    }
    

    使用该设置,计划任务的测试上下文类似于它在生产中运行时的情况,即 servletRequest 为 null :然后我们可以调整 Oauth2 配置,使其在这种情况下不会失败。

    【讨论】:

      【解决方案2】:

      @SpringBootTest 使用WebEnvironment.MOCK 作为webEnvironment 的默认配置值。

      如果您不想针对 WebApplicationContext 进行测试,您可以覆盖此值:

      @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
      

      【讨论】:

      • 谢谢。我这样做的问题是我需要的一些 Oauth2 组件没有被 Spring 加载。我的计划任务将调用其他一些服务,因此需要注入 org.springframework.security.oauth2.client.registration.ClientRegistrationRepository。所以现在应用程序上下文加载失败。所以我需要 WebEnvironment 的那一部分,而不是另一部分;-/
      • 如果只是缺少一组自动配置(并且您知道自己需要哪些),您可以使用 @Import(OAuth2AutoConfigration.class) 手动将它们添加到您的测试中,并根据您的需要自定义上下文。
      • OAuth2ClientAutoConfiguration 有 @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) 。从我所见,手动导入它不会绕过条件......我以为我会导入 OAuth2ClientAutoConfiguration 导入的2个配置类,但它们是包私有的,所以我不能直接导入它们。我通过创建自己的 OAuth2ClientAutoConfiguration 没有条件尝试了一个丑陋的黑客攻击,但在同一个包中,即 org.springframework.boot.autoconfigure.security.oauth2.client.servlet ,但我也被困在那里
      • 我明白了。不使用WebApplicationContext 运行的主要原因是什么?如果您想避免自动配置,则可以反过来明确禁用此配置。如果你想要一个真实的 ServletContext 而不是一个模拟的,你可以为webEnvironment 选择RANDOM_PORT,Spring Boot 将启动嵌入式 Tomcat。
      • 这不是端口问题。我在stackoverflow.com/questions/69885575/… 中用不同的细节问了这个问题,但没有得到回应:在测试作为 web 应用程序一部分的计划任务时,SpringBootTest 默认设置的上下文与它在运行时,因此该任务在 prod 中失败,但在测试中通过。问题是它不是“拧”它的自动配置,而是 ServletTestExecutionListener :如果我可以禁用它,我相信我会没事的
      猜你喜欢
      • 2016-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-10
      • 1970-01-01
      相关资源
      最近更新 更多