Тестирование Apache сервлета Camel с контрактом Spring Cloud - PullRequest
0 голосов
/ 06 августа 2020

У меня есть приложение для весенней загрузки с маршрутами, определенными следующим образом:

@Component
public class SampleRoute extends RouteBuilder {
 @Override
public void configure() throws Exception {

 rest("/customers-controller/")
.get("/customers").to("direct:getcustomer)
.get("/{id}").to("direct:customerDetail")
.get("/{id}/orders").to("direct:customerOrders")
.post("/neworder").to("direct:customerNewOrder");
}

Я пытаюсь создать контракт для тестирования этой конечной точки ('/ customers') и создать заглушку, которая будет использоваться в моем потребительском классе.

Контракт для служб обмена сообщениями, таких как camel, похож на этот:

    Contract.make {
    label("positive")
    input {
        messageFrom("seda:getcustomer")
        messageBody([
                id: "25_body"
        ])
        messageHeaders {
            messagingContentType(applicationJson())
//            header("id","123_header")
        }
    }
    outputMessage {
        sentTo("seda:iris-address-int")
        body([
                        "id":"25_body","firstname":null,"lastname":null,"email":null,"phone":null,"street":null,"city":null,"postcode":null
        ]
        )
        headers {
            messagingContentType()
        }
    }
}

Теперь я не уверен в том, как определить контракт так, чтобы он указывал на моя выбранная конечная точка отдыха, как я бы сделал с RestController.

Рассмотрим тест ниже. Можно ли сгенерировать этот тест на стороне провайдера с использованием весеннего облачного контракта, учитывая, что я использую не компонент @RestController, а компонент rest?

@RunWith(CamelSpringBootRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)


public class TestRestRoute {

    @Autowired
    private TestRestTemplate restTemplate;
    @LocalServerPort
    int randomServerPort

 @Test
    public void test_bank_route() throws URISyntaxException, IOException {
        //call the REST API
       
        final String baseUrl = "http://localhost:"  + randomServerPort + "/customers-controller/customers";
        URI uri = new URI(baseUrl);
       
        ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class );
        Assert.assertEquals(MediaType.APPLICATION_JSON_VALUE, Objects.requireNonNull(response.getHeaders().getContentType()).toString());
        Assert.assertEquals(200, response.getStatusCodeValue());
        Assert.assertNull(response.getBody());
    }

1 Ответ

2 голосов
/ 06 августа 2020

С этим коммитом https://github.com/spring-cloud-samples/spring-cloud-contract-samples/commit/760d7105bde2f253ca0bff84fa3f4d5a35a1931e Я добавил образец для Camel в ветку контракта Spring Cloud 3.0.x. Конечно, те же правила применяются к Spring Cloud Contract в других версиях. В общем, то, что вы можете сделать, находится на стороне производителя:

Определите конфигурацию для маршрута и извлеките URI компонентов для разделения методов:

@Configuration
class RouteConfiguration {

    @Bean
    RoutesBuilder myRouter() {
        return new RouteBuilder() {
            @Override
            public void configure() {
                from(start())
                        .bean(MyProcessor.class)
                        .to(finish());
            }
        };
    }

    // rabbitmq://hostname[:port]/exchangeName?[options]
    String start() { return "rabbitmq:localhost/person?queue=person"; }

    String finish() {
        return "rabbitmq:localhost/verifications?queue=verifications";
    }

}

Однако в контракте мы будем использовать seda компонент (как вы это сделали)

Contract.make {
    label("positive")
    input {
        messageFrom("seda:person")
        messageBody([
                age: 25
        ])
        messageHeaders {
            messagingContentType(applicationJson())
        }
    }
    outputMessage {
        sentTo("seda:verifications")
        body([
                eligible: true
        ])
        headers {
            messagingContentType(applicationJson())
        }
    }
}

Теперь в базовом классе для сгенерированных тестов мы изменим конфигурацию, чтобы отразить то, что у нас есть в контракте

package com.example.demo;

import org.apache.camel.test.spring.CamelSpringRunner;
import org.junit.runner.RunWith;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;

@RunWith(CamelSpringRunner.class)
@SpringBootTest(classes = BaseClass.TestConfiguration.class)
// IMPORTANT
@AutoConfigureMessageVerifier
// IMPORTANT
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public abstract class BaseClass {

    @Configuration
    @EnableAutoConfiguration
    static class TestConfiguration extends RouteConfiguration {

        // was:     rabbit
        // will be: a queue
        @Override
        String start() {
            return "seda:person";
        }

        @Override
        String finish() {
            return "seda:verifications";
        }
    }
}

Мы переопределяем методы start() и finish(). Вы могли бы сделать что-то подобное в своем случае. Затем на стороне потребителя вы можете ссылаться на него следующим образом:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.camel.CamelContext;
import org.apache.camel.ConsumerTemplate;
import org.apache.camel.ProducerTemplate;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.test.annotation.DirtiesContext;

@SpringBootTest
@AutoConfigureStubRunner(
        ids = "com.example:beer-api-producer-camel",
        stubsMode = StubRunnerProperties.StubsMode.LOCAL
)
// IMPORTANT
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class DemoApplicationTests {

    @Autowired ConsumerTemplate consumerTemplate;
    @Autowired ProducerTemplate producerTemplate;
    @Autowired CamelContext camelContext;
    ObjectMapper objectMapper = new ObjectMapper();

    // consumer -> seda:person
    //  producers -> seda:person -> person -> verifications -> seda:verifications
    // consumer -> seda:verifications

    @BeforeEach
    public void setup() {
        this.camelContext.getShutdownStrategy().setTimeout(1);
    }

    @Test
    public void should_trigger_a_negative_verification() throws Exception {
        this.producerTemplate.sendBodyAndHeader("seda:person", new Person(17),
                "contentType", "application/json");

        String string =
                this.consumerTemplate.receiveBody("seda:verifications", String.class);
        Verification verification = this.objectMapper.readerFor(Verification.class).readValue(string);
        BDDAssertions.then(verification).isNotNull();
        BDDAssertions.then(verification.eligible).isFalse();
    }

    @Test
    public void should_trigger_a_positive_verification() throws Exception {
        this.producerTemplate.sendBodyAndHeader("seda:person", new Person(25),
                "contentType", "application/json");

        String string =
                this.consumerTemplate.receiveBody("seda:verifications", String.class);
        Verification verification = this.objectMapper.readerFor(Verification.class).readValue(string);
        BDDAssertions.then(verification).isNotNull();
        BDDAssertions.then(verification.eligible).isTrue();
    }

}
...