首页 > 解决方案 > 测试 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));
            });
    }
}

它工作正常但是我不确定这是否足以测试这个端点......

干杯

标签: springspring-webfluxreactivespring-webclient

解决方案


推荐阅读