【问题标题】:Unit test Spring Cloud Gateway RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder)单元测试 Spring Cloud Gateway RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder)
【发布时间】:2020-08-31 14:14:30
【问题描述】:

我想用 JUnit5 正确地对 Spring Cloud Gateway RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) { 方法进行单元测试。

但是,我很难弄清楚要测试什么、断言什么、模拟什么、如何提高覆盖率等等...... 如果可能的话,我只想对此进行单元测试,不需要启动整个 SpringTest 等。

@Bean
    @Override
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        return routeLocatorBuilder.routes()
                .route("forward_to_service_one", r -> r.path("/serviceone/**").and().uri("http://the-first-service:8080"))
                .route("forward_to_service_two", r -> r.path("/servicetwo/**").and().uri("http://the-second-service:8080"))
                .route("forward_to_service_three", r -> r.alwaysTrue().and().order(Ordered.LOWEST_PRECEDENCE).uri("http://the-default-third-service:8080"))
                .build();
    }

在使用集成测试时,点击在端点上启动的网关服务,看到转发到相应服务的请求,我想知道是否有一个好的实践来测试这个 Spring Cloud Gateway 功能。

请问有什么完全覆盖的测试用例的例子吗?

谢谢

【问题讨论】:

    标签: java spring unit-testing junit5 spring-cloud-gateway


    【解决方案1】:

    我无法理解您的测试场景(您想测试什么,是否为路径正确配置了服务?)但我想向您展示两种方法,第一种是基本的,第二种是更多的如果您需要更多控制,则复杂的。

    简单

    这很简单,我正在向我的 SpringBootTest 属性添加一些路由,我使用 Spring 提供给我的 WebTestClient 实用程序来针对 Netty 进行响应式测试。然后在我的测试中,我只是向这个 /test 端点发送请求并期望它已配置(根据您的实现,如果您不扩展 spring 云网关,我可以说这个测试是无用的,我们不应该测试spring cloud gateway的功能,但无论如何这是我从你的描述中理解的)

    @RunWith(SpringRunner.class)
    @SpringBootTest(properties = {
        "spring.cloud.gateway.routes[0].id=test",
        "spring.cloud.gateway.routes[0].uri=http://localhost:8081",
        "spring.cloud.gateway.routes[0].predicates[0]=Path=/test/**",
    }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class NettyRoutingFilterTests {
    
        @Autowired
        private ApplicationContext context;
    
        @Test
        @Ignore
        public void mockServerWorks() {
            WebTestClient client = WebTestClient.bindToApplicationContext(this.context)
                    .build();
            client.get().uri("/test").exchange().expectStatus().isOk();
        }
    

    复杂

    所以第二种方法可能是;将您的模拟路由定位器设置为源代码中的上下文并调用您的服务,断言您的响应。当您出于某种原因需要一些控制时,这与从 SpringBootProperties 设置路由不同(在我的情况下,我们使用的是合同测试,我不会详细介绍),但这里有一些我没有尝试完整示例的模拟(但是在我的项目中使用相同的方法),但它应该会给你一些想法和一些起点;

    @ExtendWith( { SpringExtension.class } )
    @SpringBootTest(classes = { MockConfigurer.class },
    webEnvironment = WebEnvironment.RANDOM_PORT )
    public class RoutingIT
    {
    
    @LocalServerPort
    private int port;
    

    你应该像下面这样模拟路由,所以这将在请求时返回我们的 ServiceInstance。在下一步中,我们还将把我们的 ServiceInstance 放到上下文中。 (我在这里使用发现客户端,我的路由是从 consul/eureka 返回的,但重要的一点是上下文中有 RouteDefinitions。如果您使用的是另一个定位器,请检查 RouteDefinitionLocator 实现并基于该向您的上下文注入相应的路由);

    @Configuration
    public class MockConfigurer
    {
        private List<ServiceInstance> services;
    
        public MockConfigurer( List<ServiceInstance> services)
        {
            this.services= services;
        }
    
        @Bean
        public DiscoveryClient discoveryClient( )
        {
            final DiscoveryClient mock = mock( DiscoveryClient.class );
            final Map<String, List<ServiceInstance>> clusters =
                this.services.stream( ).collect( Collectors.groupingBy( ServiceInstance::getServiceId ) );
            given( mock.getServices( ) ).willReturn( new ArrayList<>( clusters.keySet( ) ) );
            clusters.forEach( ( clusterId, services ) -> given( mock.getInstances( clusterId ) ).willReturn( services ) );
            return mock;
        }
    }
    

    现在在您的测试中实现一个 MockService;

    public class MockService implements ServiceInstance
    {
        // fields, constructors
    
        @Override
        public String getServiceId( )
        {
            return id;
        }
    
        @Override
        public int getPort( )
        {
            return port;
        }
    
        // and other functions as well, but you will get the point
    

    在您的测试中创建此 MockService 的实例并将它们注入到 spring 上下文中,以便可以将它们作为服务发现我们之前的 MockConfigurer;

    @Bean
    public static MockService mockClusterInstance1( )
    {
        return new MockService("test", 8081, // more fields based on your implementation, also pay attention this is what we defined in the @SpringBootTest annotation);
    }
    

    现在一切都准备好进行测试了。

    @Test
    public void should_GetResponseFromTest_WhenCalled( ) throws Exception
    {
        URI uri= new URI( "http://localhost:" + this.port+ "/test");
        ResponseEntity<String> res = this.restTemplate.getForEntity( uri, String.class );
        assertThat( res.getStatusCodeValue( ) ).isEqualTo( HttpURLConnection.HTTP_OK );
        assertThat( res.getBody( ) ).isEqualTo( // your expectation );
    

    【讨论】:

      猜你喜欢
      • 2020-01-11
      • 2019-11-17
      • 1970-01-01
      • 2020-04-13
      • 1970-01-01
      • 2018-02-20
      • 1970-01-01
      • 2021-07-23
      • 2020-03-01
      相关资源
      最近更新 更多