Integración de aplicaciones web con Spring Cloud Contract


Este artículo se enfocará en la integración de aplicaciones web escritas usando Spring y trabajando sobre HTTP. El nombre de Spring Cloud Contract, en mi opinión, es engañoso, ya que no tiene nada que ver con la nube.


Se tratará de contratos de API.


Para las pruebas unitarias de controladores, a menudo se utilizan mockMCV o RestAssured. Para los mokas en el lado frontal, se utilizan servidores mosk, como Wiremock o Pact. Pero a menudo, las pruebas unitarias son escritas por algunas personas y otras por moki.


Esto puede conducir a problemas con la integración.


Por ejemplo, un servidor en ausencia de datos puede devolver 204 NO_CONTENT, y un cliente puede esperar 200 OK y json vacío.


No importa cuál de ellos sea el correcto. El problema es que alguien cometió un error y no se encontrará antes de la etapa de integración.


Este problema está diseñado para resolverse mediante un contrato de nube de primavera.


¿Qué es el contrato de nube de primavera?


Este es un archivo en el que el dialecto yaml o groovyDSL describe cómo debe verse la solicitud y la respuesta. Por defecto, todos los contratos están en la carpeta /src/test/resources/contracts/* .


Por ejemplo, pruebe el punto final GET más simple


 @GetMapping("/bets/{userId}") public ResponseEntity<List<Bet>> getBets(@PathVariable("userId") String userId) { List<Bet> bets = service.getByUserId(userId); if (bets.isEmpty()) { return ResponseEntity.noContent().build(); } return ResponseEntity.ok(bets); } 

Describiremos el contrato.


 org.springframework.cloud.contract.spec.Contract.make { request { method 'GET' urlPath '/bets/2' } response { status 200 headers { header('Content-Type', 'application/json') } body(''' { "sport":"football", "amount": 1 } ''' ) } } 

Además, las pruebas unitarias y json para wiremock se generan a partir de este archivo utilizando el complemento maven o gradle.


La descripción JSON del simulacro para el ejemplo anterior se verá así:


 { "id" : "df8f7b73-c242-4664-add3-7214ac6356ff", "request" : { "urlPath" : "/bets/2", "method" : "GET" }, "response" : { "status" : 200, "body" : "{\"amount\":1,\"sport\":\"football\"}", "headers" : { "Content-Type" : "application/json" }, "transformers" : [ "response-template" ] }, "uuid" : "df8f7b73-c242-4664-add3-7214ac6356ff" } 

Wiremock se puede ejecutar localmente, solo necesita descargar el jar desde aquí . Por defecto, json mokee debe poner la carpeta de mappings .


 $java -jar wiremock-standalone-2.18.0.jar 

A continuación se muestra la prueba generada. La biblioteca RestAssured se usa de manera predeterminada, pero se puede usar mockMVC o spockframework.


 public class UserControllerTest extends ContractBae { @Test public void validate_get_200() throws Exception { // given: MockMvcRequestSpecification request = given(); // when: ResponseOptions response = given().spec(request) .get("/bets/2"); // then: assertThat(response.statusCode()).isEqualTo(200); assertThat(response.header("Content-Type")).isEqualTo("application/json"); // and: DocumentContext parsedJson = JsonPath.parse(response.getBody().asString()); assertThatJson(parsedJson).field("['amount']").isEqualTo(1); assertThatJson(parsedJson).field("['sport']").isEqualTo("football"); } } 

Cabe señalar que todas las clases generadas heredan alguna clase base (puede haber varias clases base), en las que se inicializan todos los parámetros necesarios para las pruebas. La ruta a la clase base se describe en la configuración del complemento.


Para este ejemplo, la clase base podría verse así:


 @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SccDemoApplication.class) public abstract class ContractBae { @LocalServerPort int port; @Autowired protected WebApplicationContext webApplicationContext; @Before public void configureRestAssured() { RestAssured.port = port; MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) .build(); RestAssuredMockMvc.mockMvc(mockMvc); } } 

Como resultado, obtenemos que tanto las pruebas como los mokeys se obtuvieron de una sola fuente. Si las pruebas unitarias pasan y el front-end condicional se ejecuta en mokas, entonces no habrá problemas con la integración.


Pero eso no es todo
Moki puede usar no solo el front-end, sino también la aplicación en sí para integrarse con otra aplicación. Spring puede iniciar un servidor mosk, solo necesita generar un jar con mokami y pasarle la ruta a las anotaciones @AutoConfigureStubRunner


Digamos que nuestro controlador hace HTTP a otra aplicación:


 @GetMapping("/bets/{userId}") public ResponseEntity<List<Bet>> getBets(@PathVariable("userId") String userId) { if(!isUsetExists(userId)) { return ResponseEntity.notFound().build(); } List<Bet> bets = service.getByUserId(userId); if (bets.isEmpty()) { return ResponseEntity.noContent().build(); } return ResponseEntity.ok(bets); } private boolean isUsetExists(String userId) { try { restTemplate.getForObject("/exists/" + userId, Void.class); return true; } catch (HttpStatusCodeException ignore) { return false; } } 

Entonces solo necesitas describir let jar with mokami en la clase base


 @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SccDemoApplication.class) @AutoConfigureStubRunner(ids = {"me.dehasi.contracts.demo:sub-service-stubs:+:stubs:8090"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) public abstract class ContractBase { 

Porque estas son pruebas de controlador, luego se pueden generar fragmentos ascii-doc a partir de las mismas pruebas (ya se encuentra un artículo completo sobre rest-docs en el concentrador ).


Que tenemos
Resulta que tenemos una fuente del contrato de API, que se describe en un lenguaje legible por humanos, y de él generamos pruebas unitarias (teóricamente también documentación), y de ahí el moki. Este enfoque reduce el riesgo de errores de integración entre aplicaciones web.


Se pueden ver ejemplos en el sitio web oficial, por ejemplo .


Los ejemplos de código en el artículo están tomados del proyecto más simple aquí .

Source: https://habr.com/ru/post/es424057/


All Articles