Введение в Spring Cloud Контракт
1. Вступление
Spring Cloud Contract - это проект, который, проще говоря, помогает нам писатьConsumer-Driven Contracts (CDC).
Это обеспечивает контракт междуProducer иConsumer в распределенной системе - как для взаимодействий на основе HTTP, так и для взаимодействий на основе сообщений.
В этой быстрой статье мы рассмотрим написание тестовых примеров на стороне производителя и потребителя для Spring Cloud Contract через HTTP-взаимодействие.
2. Производитель - на стороне сервера
Мы собираемся написать CDC на стороне производителя в формеEvenOddController, который просто сообщает, является ли параметрnumber четным или нечетным:
@RestController
public class EvenOddController {
@GetMapping("/validate/prime-number")
public String isNumberPrime(@RequestParam("number") Integer number) {
return Integer.parseInt(number) % 2 == 0 ? "Even" : "Odd";
}
}
2.1. Maven Зависимости
Для стороны производителя нам понадобится зависимостьspring-cloud-starter-contract-verifier:
org.springframework.cloud
spring-cloud-starter-contract-verifier
2.1.1.RELEASE
test
И нам нужно настроитьspring-cloud-contract-maven-plugin с именем нашего базового тестового класса, который мы опишем в следующем разделе:
org.springframework.cloud
spring-cloud-contract-maven-plugin
2.1.1.RELEASE
true
com.example.spring.cloud.springcloudcontractproducer.BaseTestClass
2.2. Настройка со стороны производителя
Нам нужно добавить базовый класс в тестовый пакет, который загружает наш контекст Spring:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@DirtiesContext
@AutoConfigureMessageVerifier
public class BaseTestClass {
@Autowired
private EvenOddController evenOddController;
@Before
public void setup() {
StandaloneMockMvcBuilder standaloneMockMvcBuilder
= MockMvcBuilders.standaloneSetup(evenOddController);
RestAssuredMockMvc.standaloneSetup(standaloneMockMvcBuilder);
}
}
In the /src/test/resources/contracts/ package, we’ll add the test stubs, например, этот в файлеshouldReturnEvenWhenRequestParamIsEven.groovy:
import org.springframework.cloud.contract.spec.Contract
Contract.make {
description "should return even when number input is even"
request{
method GET()
url("/validate/prime-number") {
queryParameters {
parameter("number", "2")
}
}
}
response {
body("Even")
status 200
}
}
Когда мы запускаем сборку,the plugin automatically generates a test class named ContractVerifierTest that extends our BaseTestClass и помещаем ее в/target/generated-test-sources/contracts/.
Имена методов тестирования являются производными от префикса «validate» _, соединенного с именами заглушек тестов Groovy. Для указанного выше файла Groovy сгенерированное имя метода будет“validate_shouldReturnEvenWhenRequestParamIsEven”.
Давайте посмотрим на этот автоматически созданный тестовый класс:
public class ContractVerifierTest extends BaseTestClass {
@Test
public void validate_shouldReturnEvenWhenRequestParamIsEven() throws Exception {
// given:
MockMvcRequestSpecification request = given();
// when:
ResponseOptions response = given().spec(request)
.queryParam("number","2")
.get("/validate/prime-number");
// then:
assertThat(response.statusCode()).isEqualTo(200);
// and:
String responseBody = response.getBody().asString();
assertThat(responseBody).isEqualTo("Even");
}
Сборка также добавит заглушку jar в наш локальный репозиторий Maven, чтобы ее мог использовать наш потребитель.
Заготовки будут находиться в папке вывода в папкеstubs/mapping/.
3. Потребитель - Клиентская сторона
The consumer side of our CDC will consume stubs generated by the producer side через HTTP-взаимодействие для поддержания контракта, поэтомуany changes on the producer side would break the contract.
Мы добавимBasicMathController,, который будет выполнять HTTP-запрос для получения ответа от сгенерированных заглушек:
@RestController
public class BasicMathController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/calculate")
public String checkOddAndEven(@RequestParam("number") Integer number) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Content-Type", "application/json");
ResponseEntity responseEntity = restTemplate.exchange(
"http://localhost:8090/validate/prime-number?number=" + number,
HttpMethod.GET,
new HttpEntity<>(httpHeaders),
String.class);
return responseEntity.getBody();
}
}
3.1. Зависимости Maven
Для нашего потребителя нам нужно добавить зависимостиspring-cloud-contract-wiremock иspring-cloud-contract-stub-runner:
org.springframework.cloud
spring-cloud-contract-wiremock
2.1.1.RELEASE
test
org.springframework.cloud
spring-cloud-contract-stub-runner
2.1.1.RELEASE
test
3.2. Настройка на стороне потребителя
Пришло время настроить наш обработчик заглушек, который будет информировать нашего потребителя о доступных заглушках в нашем локальном репозитории Maven:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
@AutoConfigureJsonTesters
@AutoConfigureStubRunner(
stubsMode = StubRunnerProperties.StubsMode.LOCAL,
ids = "com.example.spring.cloud:spring-cloud-contract-producer:+:stubs:8090")
public class BasicMathControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
public void given_WhenPassEvenNumberInQueryParam_ThenReturnEven()
throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/calculate?number=2")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string("Even"));
}
}
Обратите внимание, что свойствоids аннотации@AutoConfigureStubRunner указывает:
-
com.example.spring.cloud -groupId нашего артефакта
-
spring-cloud-contract-producer -artifactId заглушки производителя
-
8090 - порт, на котором будут работать сгенерированные заглушки
4. Когда контракт разорван
Если мы внесем какие-либо изменения на стороне производителя, которые напрямую влияют на контракт, без обновления стороны потребителя,this can result in contract failure.
Например, предположим, что мы должны изменить URI запросаEvenOddController на/validate/change/prime-number на стороне производителя.
Если мы не сможем проинформировать нашего потребителя об этом изменении, потребитель все равно отправит свой запрос на URI/validate/prime-number, а тестовые примеры на стороне потребителя выдадутorg.springframework.web.client.HttpClientErrorException: 404 Not Found.
5. Резюме
Мы увидели, как Spring Cloud Contract может помочь нам поддерживать контракты между потребителем и производителем сервиса, чтобы мы могли выпускать новый код, не беспокоясь о разрыве контрактов.
И, как всегда, полную реализацию этого руководства можно найти вover on GitHub.