Integration von Webanwendungen in Spring Cloud Contract


Dieser Artikel konzentriert sich auf die Integration von Webanwendungen, die mit Spring geschrieben wurden und über HTTP arbeiten. Der Name Spring Cloud Contract ist meiner Meinung nach irreführend, da er nichts mit Cloud zu tun hat.


Es geht um API-Verträge.


Für Unit-Tests von Controllern werden häufig mockMCV oder RestAssured verwendet. Für Mokas auf der Front-End-Seite werden Mosk-Server wie Wiremock oder Pact verwendet. Aber oft werden Unit-Tests von einigen Leuten und andere von Moki geschrieben.


Dies kann zu Integrationsproblemen führen.


Beispielsweise kann ein Server ohne Daten 204 NO_CONTENT zurückgeben, und ein Client kann 200 OK und leeren json erwarten.


Es spielt keine Rolle, welcher von ihnen richtig ist. Das Problem ist, dass jemand einen Fehler gemacht hat und dieser vor der Integrationsphase nicht gefunden wird.


Dieses Problem soll durch einen Spring Cloud-Vertrag gelöst werden.


Was ist Frühlingswolkenvertrag


Dies ist eine Datei, in der der Dialekt yaml oder groovyDSL beschreibt, wie die Anforderung und Antwort aussehen sollen. Standardmäßig befinden sich alle Verträge im Ordner /src/test/resources/contracts/* .


Testen Sie beispielsweise den einfachsten GET-Endpunkt


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

Wir werden den Vertrag beschreiben


 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 } ''' ) } } 

Außerdem werden aus dieser Datei unter Verwendung des Maven- oder Gradle-Plugins Unit-Tests und JSons für Wiremock generiert.


Die JSON-Beschreibung des Modells für das obige Beispiel sieht folgendermaßen aus:


 { "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 kann lokal ausgeführt werden. Sie müssen nur das Glas von hier herunterladen. Standardmäßig muss json mokee den mappings Ordner ablegen.


 $java -jar wiremock-standalone-2.18.0.jar 

Unten ist der generierte Test dargestellt. Die RestAssured-Bibliothek wird standardmäßig verwendet, es können jedoch mockMVC oder spockframework verwendet werden.


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

Es ist zu beachten, dass alle generierten Klassen eine Basisklasse erben (es können mehrere Basisklassen vorhanden sein), in der alle für die Tests erforderlichen Parameter initialisiert werden. Der Pfad zur Basisklasse wird in den Plugin-Einstellungen beschrieben.


In diesem Beispiel könnte die Basisklasse folgendermaßen aussehen:


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

Als Ergebnis erhalten wir, dass sowohl Tests als auch Mokeys aus einer Hand erhalten wurden. Wenn Unit-Tests bestanden werden und das bedingte Front-End auf Mokas ausgeführt wird, gibt es keine Probleme mit der Integration.


Aber das ist noch nicht alles
Moki kann nicht nur das Front-End, sondern auch die Anwendung selbst verwenden, um sie in eine andere Anwendung zu integrieren. Spring kann einen Mosk-Server starten. Sie müssen lediglich ein Glas mit Mokami generieren und den Pfad zu diesen @AutoConfigureStubRunner Annotationen übergeben


Angenommen, unser Controller führt HTTP für eine andere Anwendung aus:


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

Dann müssen Sie nur noch das Glas mit Mokami in der Basisklasse beschreiben


 @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 { 

Weil Dies sind Controller-Tests. Dann können aus denselben Tests ASCII-Doc-Snippets generiert werden (ein vollständiger Artikel zu Rest-Docs befindet sich bereits auf dem Hub ).


Was haben wir?
Es stellt sich heraus, dass wir eine Quelle des API-Vertrags haben, die in einer für Menschen lesbaren Sprache beschrieben ist, und daraus Generiertests (theoretisch auch Dokumentation) und daraus das Moki generieren. Dieser Ansatz reduziert das Risiko von Integrationsfehlern zwischen Webanwendungen.


Beispiele können zum Beispiel auf der offiziellen Website eingesehen werden.


Die Codebeispiele im Artikel stammen aus dem einfachsten Projekt hier .

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


All Articles