使用 spring 云合约测试 Apache Camel servlet

Testing Apache Camel servlet with spring cloud contract

我有一个 spring 引导应用程序,其路由定义如下:

@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')并创建一个将在我的消费者中使用的存根 class。

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 组件,是否可以使用 spring 云合同在提供商端生成此测试?

@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());
    }

通过此提交 https://github.com/spring-cloud-samples/spring-cloud-contract-samples/commit/760d7105bde2f253ca0bff84fa3f4d5a35a1931e 我已将 Camel 示例添加到 spring 云合同分支 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())
        }
    }
}

现在,在生成测试的基础 class 中,我们将更改配置以反映我们在合约中的内容

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();
    }

}