Облачный контракт Spring и маршрутизация webflux

Я пытаюсь использовать весенний облачный контракт с сервисом отдыха, используя маршрутизацию Spring 5, но это не работает. Я на стороне клиента, и я пытаюсь использовать программу-заглушку в тесте junit. Если я использую классический @RestController и поток, он работает нормально, но если я попытаюсь изменить контроллер с помощью 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>

Routing.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. Если я добавляю только зависимость от заглушки, я считаю, что это поведение, я нахожу эту проблему. Я также пытался использовать последнюю версию весенней загрузки и весеннего облачного контракта, но у меня та же проблема. кто-нибудь может мне помочь?

Я удалил @AutoConfigureStubRunner, и он отлично работал с маршрутами.

Kevin Hussey 05.10.2018 02:00

хорошо, но как ты использовал заглушку? мне нужно использовать заглушку, чтобы сделать потребительский тест

Luca 05.10.2018 09:24
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
2
744
1

Ответы 1

Spring Cloud Contract Stub Runner просто запускает сервер WireMock на заданном (или случайном порте). Ничего связанного с WebTestClient не происходит с Stub Runner. Другими словами, скорее всего, вы неправильно сконфигурировали WebTestClient.

Попробуем убедиться, что вы не злоупотребляете проектом. Если у вас есть служба A, вызывающая службу B через WebClient, тогда для службы B должны быть определены контракты, из которых будут создаваться тесты и интервалы. Затем на стороне службы A вы будете использовать Spring Cloud Contract Stub Runner для запуска заглушек службы B. Что бы вы ни использовали (RestTemplate, WebClient, что угодно), вы все равно будете отправлять HTTP-вызов на сервер WireMock, который мы запускаем для вас.

Пример использования 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
}

я думаю, что это то, что я сделал. Я создаю отличный контракт на стороне производителя, я запускаю команду mvn install, чтобы сгенерировать заглушку. в моем потребителе у меня есть служба, которая вызывает производителя по указанному URL-адресу. Я настроил программу-заглушку для запуска по этому URL-адресу и запуска теста. где я не прав? У меня есть 404, даже если я удалю аннотацию автонастройки stub runner, но у меня есть stub runner starter в моей зависимости

Luca 05.10.2018 10:03

я уже видел этот пример. но я на стороне потребителя, а не производителя

Luca 05.10.2018 13:23

Обновил ответ с помощью прохождения теста, который я добавил в образцы

Marcin Grzejszczak 05.10.2018 14:22

я попробую сделать другой тест. но моя конфигурация веб-клиента - это то, что вы видите в моем первом посте. Если я создаю тест, который вызывает только потребителя, он работает хорошо. когда я добавляю зависимость от заглушки (без добавления конфигурации в тест), тест завершается неудачно. Спасибо за ответы

Luca 05.10.2018 16:43

Спасибо @MarcinGrzejszczak. Не могли бы вы помочь мне понять, следует ли мне издеваться над контроллером / службами или вводить настоящий? В примерах вы приводите реальную реализацию, поэтому запутались. Нашему сервису требуется подключение к базе данных.

Denys 14.04.2020 19:04

Вы должны предоставить реальные контроллеры с имитацией сервисов (пример: github.com/spring-cloud-samples/spring-cloud-contract-sample‌ s /…, где мы вводим лямбду с фальшивым сервисом)

Marcin Grzejszczak 20.05.2020 10:04

Другие вопросы по теме