java - 单元测试 Spring Cloud Gateway RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder)

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

我想使用 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 功能的良好实践。

有没有完全覆盖的测试用例的例子?

谢谢

最佳答案

我无法理解你的测试场景(你想测试什么,如果路径的服务配置正确或?)但我想告诉你两种方法,第一种是基本的,第二种是更多的如果您需要更多的控制,那么复杂的一个。

简单

这将很简单,我正在向我的 SpringBootTest 属性添加一些路由,我使用 Spring 提供给我的 WebTestClient 实用程序来对 Netty 进行响应式测试。然后在我的测试中,我只是向这个 /test 端点发送请求并期望它已配置(根据你的实现,如果你不扩展 spring cloud gateway 我可以说这个测试是无用的,我们不应该测试 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 设置路由不同(在我的例子中,我们使用的是契约(Contract)测试,我不会详细介绍),但这里有一些模拟,我没有尝试完整的例子(但是在我的项目中使用相同的方法)但它应该为您提供想法和一些起点;

@ExtendWith( { SpringExtension.class } )
@SpringBootTest(classes = { MockConfigurer.class },
webEnvironment = WebEnvironment.RANDOM_PORT )
public class RoutingIT
{

@LocalServerPort
private int port;

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

@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 的实例并将它们注入(inject)到 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 );

关于java - 单元测试 Spring Cloud Gateway RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63672491/

相关文章:

java - Maven安装j2ee文件夹结构问题(spring,struts..)

java - CSV 文件生成

python - django 运行 unittest 失败,而我的一个名为 "apps"的应用程序,但 "runserver"有效

java - Sonar 产生警告,Eclipse 无法使用格式化程序解决

java - 如果显示recyclerView,如何停止显示加载?

java - 我的 Jlists 没有发生滚动

ios - 为什么 UIView 在手动实例化到模拟 UIViewController 后为 nil?

java - 线程中出现异常 "AWT-EventQueue-0"java.lang.UnsatisfiedLinkError : testmydll. TestDLL.a()I

java - 在使用执行器服务创建的线程中使用声明性事务

android - 在运行我的 Cucumber 场景时,类没有公共(public)构造函数 TestCase(String name) 或 TestCase()