Spring Boot2功能端点WebFluxTest不匹配PUT-with-ID和DELETE-for-id({id})URL

时间:2018-03-19 03:13:57

标签: java spring spring-boot spring-webflux spring-boot-test

我正在开发一个使用功能端点开发的Spring boot2应用程序。路由器附带了一些过滤器(HandlerFilterFunction<ServerResponse, ServerResponse>的实现)。我正试图测试我遇到的问题,并提出这个问题。

使用框架

  • Spring Boot 2.0.0.RELEASE
  • TestNG的

过滤我要测试的内容

public class ResourceParsingFilter implements HandlerFilterFunction<ServerResponse, ServerResponse> {

    /**
     * {@inheritDoc}
     */
    @Override
    public Mono<ServerResponse> filter(ServerRequest request, HandlerFunction<ServerResponse> next) {
        final HttpMethod httpMethod = request.method();
        final String requestPath = request.path();

        // Collect the resource id and type from path
        final ResourceDetail resourceDetail = WebUtilsFunctions.FN_GET_RESOURCE_DETAIL_FROM_PATH.apply(requestPath);

        switch (httpMethod) {
        case GET:
            if (!request.queryParam("q").isPresent() && StringUtils.isBlank(resourceDetail.getResourceId())) {
                return Mono.error(
                        new AppException(Exceptions.APP_400002.exceptionCode(), 
                                WebUtilsFunctions.FN_FORMAT_STRING.apply(Exceptions.APP_400002.exceptionMessage(), new String[] {httpMethod.name()})));
            }
            break;
        case PUT:
        case DELETE:
            if (StringUtils.isBlank(resourceDetail.getResourceId())) {
                return Mono.error(
                        new AppException(Exceptions.APP_400002.exceptionCode(), 
                                WebUtilsFunctions.FN_FORMAT_STRING.apply(Exceptions.APP_400002.exceptionMessage(), new String[] {httpMethod.name()})));
            }
            break;
        default:
            break;
        }

        // Store resourceDetail in request
        request.attributes().put("RESOURCE_DETAIL", resourceDetail);

        return next.handle(request);
    }

}

模拟应用配置以测试过滤器

@Configuration
@EnableWebFlux
public class MockAppConfig {
    @Bean
    public ResourceParsingFilter resourceParsingFilterBean() {
        return new ResourceParsingFilter();
    }

    @Bean // This is my own custom exception handler implementation
    public GlobalExceptionHandler globalExceptionHandler(final ApplicationContext applicationContext,
            final ServerCodecConfigurer serverCodecConfigurer) {
        return new GlobalExceptionHandler(applicationContext, serverCodecConfigurer);
    }
}

模拟路由器配置(我根据Spring Boot团队在其gitter频道(https://github.com/spring-projects/spring-boot/issues/6602)中的建议单独创建了这个类

@Configuration
public class MockResourceParsingRouterConfig {
    @Autowired
    private ResourceParsingFilter resourceParsingFilter;

    @Bean("resourceParsingFilterRoutes")
    public RouterFunction<ServerResponse> resourceParsingFilterRoutes() {
        // Dummy route configuration for testing ResourceParsingFilter
        return RouterFunctions.route(RequestPredicates.GET("/resourceParsing/{id}"), req -> ServerResponse.ok().build())
                .andRoute(RequestPredicates.PUT("/resourceParsing/{id}"), req -> ServerResponse.ok().build())
                .andRoute(RequestPredicates.DELETE("/resourceParsing/{id}"), req -> ServerResponse.ok().build())
                .andRoute(RequestPredicates.GET("/resourceParsing/"), req -> ServerResponse.ok().build()) // GET Search URL
                .filter(resourceParsingFilter);
    }
}

测试类

@Test(groups = "resourceParsingFilterTests")
@WebFluxTest
@Import({MockAppConfig.class, MockResourceParsingRouterConfig.class})
public class ResourceParsingFilterTest extends AbstractTestNGSpringContextTests {

    @Autowired
    private WebTestClient webTestClient;

//  ====== Passing tests
    @Test
    public void get_withoutId() {
        webTestClient.get().uri("/resourceParsing/")
            .exchange() // Fire http request
            .expectStatus() // Assert on HTTP Status
            .isBadRequest()
            ;
    }

    @Test(dependsOnMethods = {"get_withoutId"})
    public void get_withoutId_searchQueryParamPresent() {
        webTestClient.get().uri("/resourceParsing/?q=id:345")
            .exchange() // Fire http request
            .expectStatus() // Assert on HTTP Status
            .isOk()
            ;
    }

    // ====== Failing tests
    @Test(dependsOnMethods = {"get_withoutId_searchQueryParamPresent"})
    public void put_withoutId() {
        webTestClient.put().uri("/resourceParsing/")
            .exchange() // Fire http request
            .expectStatus() // Assert on HTTP Status
            .isBadRequest()
            ;
    }
}

测试put_withoutId失败,因为(模拟)服务器正在抛出404而不是预期400

请检查GitHub repo https://github.com/nnanda2016/router-fn-demo以获取完整代码(如果您愿意),这是一个与我的真实应用程序在同一行的演示应用程序(由于客户机密性合规性,我无法共享真实代码)。

有人可以就如何解决这个问题给我一些指示吗?

1 个答案:

答案 0 :(得分:1)

@WebFluxTest注释不支持RouterFunction的测试,只支持WebFlux注释控制器的测试。

@WebFluxTest@WebMvcTest是关于将单个带注释的控制器测试为没有完整Web基础结构的Web组件。 RouterFunction通常依赖于多个处理程序来定义实际的函数 - 所以我们不能在没有对路由进行硬性假设的情况下一致地测试单个处理程序,或者要求开发人员设计他们的架构以拥有多个RouterFunction bean。

这将更好地记录once this Spring Boot issue is resolved

与此同时,您可能应该使用@SpringBootTest;如果您不要求特定端口,此集成将配置完整的Web堆栈,但不会启动任何服务器。