spring - 测试 WebFlux 功能路由
问题描述
我试图弄清楚如何最好地测试从路由部分开始的简单 WebFlux 直通服务。
(整个小项目可在https://github.com/ChambreNoire/passthru获得)
我有一个简单的功能路由设置,如下所示:
@Configuration
@RequiredArgsConstructor
public class RouterConfig {
private final RouteHandler handler;
@Bean
public RouterFunction<ServerResponse> passthru() {
return route(path("/service/**"), this.handler::doPassthru);
}
// ...
}
由以下处理程序处理:
@Component
@RequiredArgsConstructor
public class RouteHandler {
private final WebClient webClient;
public Mono<ServerResponse> passthru(ServerRequest request) {
return webClient.method(request.method())
.uri(request.path())
.headers(httpHeaders -> request.headers().asHttpHeaders().forEach(httpHeaders::put))
.body(request.bodyToMono(PooledDataBuffer.class), PooledDataBuffer.class)
.exchangeToMono(response -> response.bodyToMono(PooledDataBuffer.class)
.flatMap(buffer -> ServerResponse
.status(response.statusCode())
.headers(headers -> headers.addAll(response.headers().asHttpHeaders()))
.bodyValue(buffer)));
}
// ...
}
使用这样定义的 WebClient :
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient(@Value("${target.url}") String targetUrl) {
return WebClient.builder()
.baseUrl(targetUrl)
.clientConnector(new ReactorClientHttpConnector(HttpClient.create()
.followRedirect(true)))
.build();
}
}
它只是通过 WebClient 将请求转发到另一台服务器并返回结果。(其他路线会处理标题等,但我在这里处理最简单的用例)。
@SpringBootTest(
classes = MyApplication.class,
webEnvironment = RANDOM_PORT
)
@RunWith(SpringRunner.class)
public class RoutingTest {
@Autowired
private RouterConfig config;
@MockBean
private RouteHandler handler;
@Test
public void passthru() {
WebTestClient testClient = WebTestClient
.bindToRouterFunction(config.passthru())
.build();
final AuthRequest authRequest = AuthRequest.builder()
.secretKey("my.secret.key")
.build();
final AccessTokens accessTokens = AccessTokens.builder()
.accountId("accountId")
.accessToken("accessToken")
.build();
final Mono<ServerResponse> response = ServerResponse.ok()
.body(Mono.just(accessTokens), AccessTokens.class);
when(handler.passthru(any(ServerRequest.class))).thenReturn(response);
testClient
.post()
.uri("/service/auth")
.body(Mono.just(authRequest), AuthRequest.class)
.exchange()
.expectStatus().isOk()
.expectBody(AuthRequest.class)
.isEqualTo(authRequest);
}
}
发送和接收的对象可以是任何东西,但为了完整起见,这里是上面使用的对象:
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class AccessTokens {
@JsonProperty("account")
private String accountId;
private String accessToken;
}
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class AuthRequest {
private String secretKey;
}
测试失败,因为返回的 MyObject 与 myObject 不同,但我不明白为什么。另外,我想捕获请求,但我不知道该怎么做。
更新!
好的,正如@Toerktumlare 所指出的,我不应该测试是否相等,但是,我仍然不确定这种方法的最佳测试方法是什么。到目前为止,我正在使用 aMockWebServer
和 a WebTestClient
:
@SpringBootTest(
classes = MyApplication.class,
webEnvironment = RANDOM_PORT
)
public class HandlerTest {
@Autowired
private RouterConfig config;
static MockWebServer mockBackEnd;
private static AuthRequest authRequest;
private static AccessTokens accessTokens;
@SuppressWarnings("unused")
@DynamicPropertySource
static void properties(DynamicPropertyRegistry registry) {
registry.add("gateway.url", () -> "http://localhost:" + mockBackEnd.getPort());
}
@BeforeAll
static void setUp() throws IOException {
mockBackEnd = new MockWebServer();
mockBackEnd.start();
authRequest = AuthRequest.builder()
.secretKey("secretKey")
.build();
accessTokens = AccessTokens.builder()
.accountId("accountId")
.accessToken("accessToken")
.renewToken("renewToken")
.type("Bearer")
.build();
}
@AfterAll
static void tearDown() throws IOException {
mockBackEnd.shutdown();
}
@Test
public void testPassthru() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
mockBackEnd.enqueue(new MockResponse()
.setResponseCode(HttpStatus.OK.value())
.setBody(objectMapper.writeValueAsString(accessTokens))
.addHeader("Content-Type", "application/json"));
WebTestClient testClient = WebTestClient
.bindToRouterFunction(config.passthru())
.build();
testClient.post()
.uri("/service/auth")
.body(Mono.just(authRequest), AuthRequest.class)
.exchange()
.expectStatus().isOk()
.expectBody(AccessTokens.class)
.value(tokens -> {
assertThat(tokens)
.returns("accountId", from(AccessTokens::getAccountId))
.returns("accessToken", from(AccessTokens::getAccessToken))
.returns("renewToken", from(AccessTokens::getRenewToken))
.returns("Bearer", from(AccessTokens::getType));
});
}
}
它工作正常但是我不确定这是否足以测试这个端点......
干杯
解决方案
推荐阅读
- azure-cosmosdb - 将数据加载到 Azure Cosmos DB (Mongo API)
- azure-devops - 无法使用 NuGet 上游源从 Azure DevOps Artifacts 源下载某些包
- xml - 如何指定这个特定的 xpath
- r - R - 如果超过 25% 的组删除观察
- go - Swagger UI 仍然显示示例
- angular - 使用 Authguard 重定向到上一页
- java - remove() 无法正常工作
- redis - 集群 Redis 6.x 转储还原到 AWS Elasticache 中的独立 redis
- c# - 尝试使用 ReadAsync 报告读取文件的进度时出现 ArgumentOutOfRange 异常
- git - Git 不断提示输入某些文件的密钥 - 多个 Git 用户