我有一个小型的spring boot 2.2批处理,它编写了一个OAuth2 REST API。
我已经能够按照https://medium.com/@asce4s/oauth2-with--webclient-761d16f89cdd配置webclient
并且它能够正常工作。
java prettyprint-override"> @Configuration
public class MyRemoteServiceClientOauth2Config {
@Bean("myRemoteService")
WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(
clientRegistrations,
new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauth.setDefaultClientRegistrationId("myRemoteService");
return WebClient.builder()
.filter(oauth)
.build();
}
}
但是,现在我想为我的批处理编写一个集成测试,我想避免使用“真正的”授权服务器来获得一个令牌:如果一个外部服务器宕机,我不希望我的测试失败。我希望我的测试是“自主”的。
在测试期间,我调用的远程服务被mockserver
假服务替换。
在这种情况下,最佳做法是什么?
@profile(“!test”)
的测试之外启用上述配置,并使用@activeprofiles(“test”)
运行我的测试。我还在测试中导入特定于测试的配置: @Configuration
@Profile("test")
public class BatchTestConfiguration {
@Bean("myRemoteService")
public WebClient webClientForTest() {
return WebClient.create();
}
}
但我觉得必须在我的生产配置中添加@profile(“!test”)
并不是很好。
@primary
放在webClientForTest bean上,但这不起作用:生产bean仍然启用,而我得到一个异常:没有“org.springframework.security.oauth2.client.registration.reactiveClientRegistrationRepository”类型的合格bean
这是生产bean需要的参数类型
我和你的处境一样,也找到了解决办法。首先,为了查看它的实际运行情况,我创建了一个存储库,其中包含下面解释的所有内容的展示实现。
有没有一种“更干净”的方法来替换我正在使用的WebClient bean,它将调用我的假远程服务,而不必先尝试获取令牌?
我不会在您的测试中替换WebClient
bean,而是将ReactiveOAuth2AuthorizedClientManager
bean替换为一个mock。为此,您必须稍微修改MyRemoteServiceClientoAuth2Config
。与使用unauthenticatedserveroauth2authorizedclientrepository
的方法相比,您可以这样配置它(这也更符合servlet堆栈上的文档配置):
@Configuration
public class MyRemoteServiceClientOauth2Config {
@Bean
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager reactiveOAuth2AuthorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2ClientCredentialsFilter =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(reactiveOAuth2AuthorizedClientManager);
oauth2ClientCredentialsFilter.setDefaultClientRegistrationId("myRemoteService");
return WebClient.builder()
.filter(oauth2ClientCredentialsFilter)
.build();
}
@Bean
public ReactiveOAuth2AuthorizedClientManager reactiveOAuth2AuthorizedClientManager(ReactiveClientRegistrationRepository clientRegistrations,
ReactiveOAuth2AuthorizedClientService authorizedClients) {
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrations, authorizedClients);
authorizedClientManager.setAuthorizedClientProvider(
new ClientCredentialsReactiveOAuth2AuthorizedClientProvider());
return authorizedClientManager;
}
}
然后可以创建ReactiveOauth2AuthorizedClientManager
的模拟,该模拟始终返回Oauth2AuthorizedClient
的Mono
,如下所示:
@TestComponent
@Primary
public class AlwaysAuthorizedOAuth2AuthorizedClientManager implements ReactiveOAuth2AuthorizedClientManager {
@Value("${spring.security.oauth2.client.registration.myRemoteService.client-id}")
String clientId;
@Value("${spring.security.oauth2.client.registration.myRemoteService.client-secret}")
String clientSecret;
@Value("${spring.security.oauth2.client.provider.some-keycloak.token-uri}")
String tokenUri;
/**
* {@inheritDoc}
*
* @return
*/
@Override
public Mono<OAuth2AuthorizedClient> authorize(final OAuth2AuthorizeRequest authorizeRequest) {
return Mono.just(
new OAuth2AuthorizedClient(
ClientRegistration
.withRegistrationId("myRemoteService")
.clientId(clientId)
.clientSecret(clientSecret)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.tokenUri(tokenUri)
.build(),
"some-keycloak",
new OAuth2AccessToken(TokenType.BEARER,
"c29tZS10b2tlbg==",
Instant.now().minus(Duration.ofMinutes(1)),
Instant.now().plus(Duration.ofMinutes(4)))));
}
}
最后@import
在您的测试中:
@SpringBootTest
@Import(AlwaysAuthorizedOAuth2AuthorizedClientManager.class)
class YourIntegrationTestClass {
// here is your test code
}
对应的src/test/resources/application.yml
如下所示:
spring:
security:
oauth2:
client:
registration:
myRemoteService:
authorization-grant-type: client_credentials
client-id: test-client
client-secret: 6b30087f-65e2-4d89-a69e-08cb3c9f34d2 # bogus
provider: some-keycloak
provider:
some-keycloak:
token-uri: https://some.bogus/token/uri
您还可以使用已经用于模拟REST资源的mockserver
来模拟授权服务器并响应令牌请求。为此,您可以将mockServer
配置为src/test/resources/application.yml
中的token-uri
或用于分别为测试提供属性的任何文件。
在bean中提供WebClient
的推荐方法是注入WebClient.builder
,它由spring boot预配置。这也保证了测试中的webclient
与生产中的配置完全相同。您可以声明WebClientCustomizer
bean来进一步配置此构建器。这就是上面提到的在我的showcase存储库中实现它的方式。
我也试过了,发现它并不总是按照预期的方式工作,可能是因为spring加载和实例化bean定义的顺序。例如,ReactiveOAuth2AuthorizedClientManager
模拟仅在@TestConfiguration
是测试类内部的静态嵌套
类时才使用,但如果它是@Import
ED则不使用。在接口上使用静态嵌套的@testconfiguration
并用测试类实现它也是不起作用的。因此,为了避免将静态嵌套
类放在我需要的每个集成测试中,我宁愿选择这里介绍的@TestComponent
方法。
我只测试了client credentials
授予类型的方法,但我认为它也可以适用于其他授予类型。
使用ScalaTest,我想替换测试用例中的函数实现。我的用例: 我想写一个单元测试,但我不希望此测试用例依赖于测试运行的实际年份。 在动态语言中,我经常使用一个可以替换函数实现以返回固定值的构造。 我希望我的测试用例更改始终返回2014,无论实际年份是什么。 我发现了几个模拟库(Mockito、ScalaMock等等),但它们都只能创建新的模拟对象。它们似乎都无法取代方法的实现。 有办法做到吗?
我试图用JUnit在测试会话期间正确使用Mockito来代替存根类。不幸的是,在网络上有很多关于Mockito的教程,但关于存根方法的教程较少,我想学习这项技术。 此测试由Mockito进行: 为了澄清这些是所涉及的类: 1) 控制器 2)回购接口: 3) 回购协议: 4) 验证器: 5) RecentyExceptionHandler
我正在使用一个具有许多不同库依赖关系的gradle项目,并使用新的清单合并。在我的
我正在使用此Composer包进行需要此包提供的功能的开发。寻找替代方案,我发现这是我最好的选择,因为可用的替代方案太简单了,无法在我处理的时间限制下实现。 现在,我已经在运行PHP 5.5的本地机器(在LinuxWindows 10的子系统上)和运行PHP 5.6的个人服务器上测试了它,但是正式服运行PHP 5.4,由于原因无法升级。 首先我犯了这个错误: 在寻找解决方案时,我遇到了这个问题,因
我很难理解Groovy单元测试中的Spock交互。 第二个测试尝试修改mock,使其为方法返回。然后,我们期望(它委托给)返回。但是,测试失败如下: Foospec:说再见:不满足26个条件 因为仍然等于。 我可能仍然在Mockito模式下思考,并假设交互等效于表达式,以后的交互将覆盖以前的交互。 是否有一种简单的方法使用Spock在方法中声明交互,然后在测试用例中重写该交互?还是需要删除方法,并
是否可以替换DI容器中的私有服务?我需要在我的测试环境中这样做,这样我就可以运行集成测试,但仍然可以模拟对外部API的HTTP调用。 例如,我有这样的代码来设置HttpClientInterface的模拟: 我已经尝试使用下面的配置将HttpClientInterface定义为我的测试环境的公共服务,但这不起作用,因为它是不可实例化的(它是一个接口)。