【问题标题】:Spring cloud contract and webflux routingSpring Cloud 合约和 webflux 路由
【发布时间】:2018-10-04 20:50:23
【问题描述】:

我正在尝试使用 spring 云合同和使用 spring 5 路由的休息服务,但它不起作用。 我在客户端,我正在尝试在 junit 测试中使用存根运行器。 如果我使用经典的@RestController 和flux,它可以正常工作,但如果我尝试使用RouterFunction 更改控制器,它不起作用并且我得到404。 这是我的示例代码。

pom.xml

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
<dependencies>
...
   <dependency>
               <groupId>org.springframework.cloud</groupId>
               <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
               <scope>test</scope>
   </dependency>
</dependencies>

路由.java

@Configuration
@EnableWebFlux
public class Routing {

    @Autowired
    private TestLoginController loginController;

    @Bean
    public HttpHandler routerFunction() {
        return WebHttpHandlerBuilder
                .webHandler(RouterFunctions.toWebHandler(createRouterFunction()))
                .build();
    }

    private RouterFunction<ServerResponse> createRouterFunction() {
        return route(POST("/testlogin"), loginController::testLogin);
    }
}

TestLoginController.java

@Component
public class TestLoginController {

    @Autowired
    private TestLoginService testLoginService;

    public Mono<ServerResponse> testLogin(ServerRequest request) {
        return Mono.just(request)
                   .flatMap(req -> ServerResponse.ok()
                                                 .body(testLoginService.testLogin(request.bodyToMono(LoginRequest.class)), LoginResponse.class)
                           );
    }
}

DemoApplicationTest.java

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureStubRunner(ids = {"groupId:artifactId:+:stubs:8090"},
        stubsMode = StubRunnerProperties.StubsMode.LOCAL)
public class DemoApplicationTests {

    @LocalServerPort
    private int port;


    @Test
    public void contextLoads() throws Exception {
        LoginRequest request = new LoginRequest();

        WebTestClient
                .bindToServer()
                .baseUrl("http://localhost:" + port)
                .build()
                .post()
                .uri("testlogin").accept(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromObject(request))
                .exchange()
                .expectStatus().isOk()
                .expectBody()
                ....
    }

}

即使我删除了@AutoConfigureStubRunner 注释,我也会遇到同样的问题。如果我只添加存根运行器依赖项,我会发现这种行为我发现了这个问题。 我也尝试使用最新版本的 spring boot 和 spring cloud contract,但我有同样的问题。谁能帮帮我?

【问题讨论】:

  • 我删除了@AutoConfigureStubRunner,它在路由上运行良好。
  • 好的,但是你是如何使用存根运行器的?我需要使用存根来进行消费者测试

标签: java spring spring-boot spring-webflux spring-cloud-contract


【解决方案1】:

Spring Cloud Contract Stub Runner 只是在给定(或随机端口)上启动 WireMock 服务器。 Stub Runner 不会发生与WebTestClient 相关的任何事情。换句话说,您很可能错误地配置了WebTestClient

让我们尽量确保您没有滥用该项目。如果您有服务 A 通过 WebClient 调用服务 B,则服务 B 应该定义合同,从中创建测试和跨度。然后在服务 A 端,您将使用 Spring Cloud Contract Stub Runner 启动服务 B 的存根。无论您使用什么(RestTemplate、WebClient 等),您仍将向我们为您启动的 WireMock 服务器发送 HTTP 调用。

如何将 Spring Cloud Contract Stub Runner 与 WebTestClient 一起使用的示例(取自:https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/master/consumer/src/test/java/com/example/BeerControllerWebClientTest.java

package com.example;
 import java.util.Objects;
 import org.junit.Test;
import org.junit.runner.RunWith;
 import org.springframework.boot.test.autoconfigure.json.AutoConfigureJsonTesters;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerPort;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
 /**
 * @author Marcin Grzejszczak
 */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
@AutoConfigureMockMvc
@AutoConfigureJsonTesters
//remove::start[]
@AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.LOCAL, ids = "com.example:beer-api-producer-webflux")
//remove::end[]
@DirtiesContext
//@org.junit.Ignore
public class BeerControllerWebClientTest extends AbstractTest {
    //remove::start[]
    @StubRunnerPort("beer-api-producer-webflux") int producerPort;
    //remove::end[]
    @Test public void should_give_me_a_beer_when_im_old_enough() throws Exception {
        //remove::start[]
        WebTestClient.bindToServer()
                .build()
                .post()
                .uri("http://localhost:" + producerPort + "/check")
                .syncBody(new WebClientPerson("marcin", 22))
                .header("Content-Type", "application/json")
                .exchange()
                .expectStatus().is2xxSuccessful()
                .expectBody(WebClientResponse.class)
                .isEqualTo(new WebClientResponse(WebClientResponseStatus.OK));
        //remove::end[]
    }
    @Test public void should_reject_a_beer_when_im_too_young() throws Exception {
        //remove::start[]
        WebTestClient.bindToServer()
                .build()
                .post()
                .uri("http://localhost:" + producerPort + "/check")
                .syncBody(new WebClientPerson("marcin", 17))
                .header("Content-Type", "application/json")
                .exchange()
                .expectStatus().is2xxSuccessful()
                .expectBody(WebClientResponse.class)
                .isEqualTo(new WebClientResponse(WebClientResponseStatus.NOT_OK));
        //remove::end[]
    }
}
 class WebClientPerson {
    public String name;
    public int age;
    public WebClientPerson(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public WebClientPerson() {
    }
}
 class WebClientResponse {
    public WebClientResponseStatus status;
    WebClientResponse(WebClientResponseStatus status) {
        this.status = status;
    }
    WebClientResponse() {
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        WebClientResponse that = (WebClientResponse) o;
        return status == that.status;
    }
    @Override
    public int hashCode() {
        return Objects.hash(status);
    }
}
 enum WebClientResponseStatus {
    OK, NOT_OK
}

【讨论】:

  • 我认为这就是我所做的。我在生产者端创建了一个 groovy 合约,我运行 mvn install 命令来生成存根。在我的消费者中,我有一项服务可以在特定的 url 上调用生产者。我将存根运行器配置为在此 url 上运行并运行测试。我哪里错了?即使我删除了存根运行器自动配置注释,我也有 404,但我的依赖项中有存根运行器启动器
  • 我已经看过这个例子了。但我是消费者而不是生产者
  • 用我添加到示例中的通过测试更新了答案
  • 我会尝试做一些其他的测试。但我的 webclient 配置是你在我的第一篇文章中看到的。如果我创建一个只调用消费者的测试,它工作得很好。当我添加存根运行器依赖项(不向测试添加配置)时,测试失败。感谢您的回复
猜你喜欢
  • 1970-01-01
  • 2019-08-03
  • 1970-01-01
  • 2017-07-23
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 2017-05-03
  • 2018-04-03
相关资源
最近更新 更多