在 HttpServletRequest 上下文之外测试 Spring Boot WebClient
test a Spring Boot WebClient outside of a HttpServletRequest context
我的 Spring boot 2.5 应用程序是一个相当标准的 Web 应用程序,它公开了一些端点并调用了其他服务。
在通常的用例中,一个外部请求进来,并应用一些业务逻辑,这将调用其他服务,使用配置了 Oauth2 的 WebClient。
但是,我的应用程序还包含一个需要调用其他服务的计划任务。
这就是问题开始的地方:我相信我遇到了与 相同的问题,即 servletRequest cannot be null
在非 Servlet 环境中使用 webClient 时。
我以为我可以通过集成测试轻松地重现它,但我没有。我已经有一个用于计划任务的 SpringBootTest
,我在其中加载 Spring 上下文,访问具有逻辑的 class,并简单地调用部署时计划的方法在生产中...但它并没有失败:我无法重现。
当我调试测试时,我看到当我 运行 测试时 servletRequest 确实不为空(当它在产品中 运行s 时):有一个 Spring MockhttpServletrequest
,所以我没有遇到与产品中相同的问题。
我已经尝试 运行 完全没有“网络”支持的测试,
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
但是当我这样做时,我的应用程序启动时缺少一些必需的元素:我在我的应用程序中配置了 2 个 webClients,一个是“正常”的,一个是我计划用于调度程序的:
@Bean
@Primary
WebClient servletWebClient(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients) {
//this constructor will configure internally a RemoveAuthorizedClientOAuth2AuthorizationFailureHandler,
// and onAuthorizationFailure will be called on it when we get a 401
var oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
oauth.setDefaultClientRegistrationId("keycloak");
return WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.apply(oauth.oauth2Configuration())
.build();
}
@Bean
@Qualifier("forScheduler")
WebClient schedulerWebClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth =new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth.setDefaultClientRegistrationId("keycloak");
return WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.apply(oauth.oauth2Configuration())
.build();
}
@Bean
public OAuth2AuthorizedClientManager authorizedClientManagerForScheduler(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService clientService)
{
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, clientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
但是当使用 webEnvironment = WebEnvironment.NONE
配置我的测试时,没有 ClientRegistrationRepository
被加载,并且声明的 servletWebClient
无法实例化,因为它是它的依赖项之一。
手动测试这个非常difficult/expensive,所以我真的很想通过我的集成测试获得某种验证,即我对 2 个 webClients 的更改在部署它之前会起作用。
我怎样才能正确测试那个场景?或者换句话说,有没有办法进行默认的 Spring 启动测试,但没有由 Spring 初始化的 mockServletContext?
我找到了使用不同方法的解决方案,使用自定义 TestExecutionListener。
更多详情见
我的 Spring boot 2.5 应用程序是一个相当标准的 Web 应用程序,它公开了一些端点并调用了其他服务。 在通常的用例中,一个外部请求进来,并应用一些业务逻辑,这将调用其他服务,使用配置了 Oauth2 的 WebClient。
但是,我的应用程序还包含一个需要调用其他服务的计划任务。
这就是问题开始的地方:我相信我遇到了与 servletRequest cannot be null
在非 Servlet 环境中使用 webClient 时。
我以为我可以通过集成测试轻松地重现它,但我没有。我已经有一个用于计划任务的 SpringBootTest
,我在其中加载 Spring 上下文,访问具有逻辑的 class,并简单地调用部署时计划的方法在生产中...但它并没有失败:我无法重现。
当我调试测试时,我看到当我 运行 测试时 servletRequest 确实不为空(当它在产品中 运行s 时):有一个 Spring MockhttpServletrequest
,所以我没有遇到与产品中相同的问题。
我已经尝试 运行 完全没有“网络”支持的测试,
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
但是当我这样做时,我的应用程序启动时缺少一些必需的元素:我在我的应用程序中配置了 2 个 webClients,一个是“正常”的,一个是我计划用于调度程序的:
@Bean
@Primary
WebClient servletWebClient(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients) {
//this constructor will configure internally a RemoveAuthorizedClientOAuth2AuthorizationFailureHandler,
// and onAuthorizationFailure will be called on it when we get a 401
var oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
oauth.setDefaultClientRegistrationId("keycloak");
return WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.apply(oauth.oauth2Configuration())
.build();
}
@Bean
@Qualifier("forScheduler")
WebClient schedulerWebClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth =new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth.setDefaultClientRegistrationId("keycloak");
return WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.apply(oauth.oauth2Configuration())
.build();
}
@Bean
public OAuth2AuthorizedClientManager authorizedClientManagerForScheduler(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService clientService)
{
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, clientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
但是当使用 webEnvironment = WebEnvironment.NONE
配置我的测试时,没有 ClientRegistrationRepository
被加载,并且声明的 servletWebClient
无法实例化,因为它是它的依赖项之一。
手动测试这个非常difficult/expensive,所以我真的很想通过我的集成测试获得某种验证,即我对 2 个 webClients 的更改在部署它之前会起作用。
我怎样才能正确测试那个场景?或者换句话说,有没有办法进行默认的 Spring 启动测试,但没有由 Spring 初始化的 mockServletContext?
我找到了使用不同方法的解决方案,使用自定义 TestExecutionListener。
更多详情见