Kontrak yang Didorong oleh Pengguna dengan Pakta

1. Gambaran keseluruhan

Dalam artikel ringkas ini, kita akan melihat konsep Kontrak Bergerak Pengguna.

Kami akan menguji integrasi dengan perkhidmatan REST luaran melalui kontrak yang kami tentukan menggunakan perpustakaan Perjanjian . Kontrak itu dapat ditentukan oleh pelanggan, kemudian diambil oleh penyedia dan digunakan untuk pengembangan perkhidmatannya.

Kami juga akan membuat ujian berdasarkan kontrak untuk aplikasi pelanggan dan penyedia.

2. Apa itu Perjanjian ?

Dengan menggunakan Pact , kita dapat menentukan harapan pengguna untuk penyedia tertentu (yang boleh menjadi perkhidmatan HTTP REST) ​​dalam bentuk kontrak (oleh itu nama perpustakaan).

Kami akan menetapkan kontrak ini menggunakan DSL yang disediakan oleh Pact . Setelah ditentukan, kami dapat menguji interaksi antara pengguna dan pembekal menggunakan perkhidmatan tiruan yang dibuat berdasarkan kontrak yang ditentukan. Juga, kami akan menguji perkhidmatan terhadap kontrak dengan menggunakan pelanggan palsu.

3. Ketergantungan Maven

Untuk memulakan, kita perlu menambahkan kebergantungan Maven ke perpustakaan pact-jvm-consumer-junit_2.11 :

 au.com.dius pact-jvm-consumer-junit_2.11 3.5.0 test 

4. Menentukan Kontrak

Apabila kita ingin membuat ujian menggunakan Pact , pertama kita harus menentukan @ Peraturan yang akan digunakan dalam ujian kita:

@Rule public PactProviderRuleMk2 mockProvider = new PactProviderRuleMk2("test_provider", "localhost", 8080, this);

Kami meneruskan nama penyedia, host dan port di mana pelayan mock (yang dibuat dari kontrak) akan dimulakan.

Katakan bahawa perkhidmatan telah menentukan kontrak untuk dua kaedah HTTP yang dapat dikendalikannya.

Kaedah pertama adalah permintaan GET yang mengembalikan JSON dengan dua medan. Apabila permintaan berjaya, ia mengembalikan kod respons HTTP 200 dan tajuk C ontent-Type untuk JSON.

Mari tentukan kontrak seperti itu menggunakan Pact .

Kita perlu menggunakan anotasi @Pact dan memberikan nama pengguna yang ditentukan kontraknya. Di dalam kaedah anotasi, kami dapat menentukan kontrak GET kami:

@Pact(consumer = "test_consumer") public RequestResponsePact createPact(PactDslWithProvider builder) { Map headers = new HashMap(); headers.put("Content-Type", "application/json"); return builder .given("test GET") .uponReceiving("GET REQUEST") .path("/pact") .method("GET") .willRespondWith() .status(200) .headers(headers) .body("{\"condition\": true, \"name\": \"tom\"}") (...) }

Dengan menggunakan Pact DSL, kami menentukan bahawa untuk permintaan GET tertentu kami ingin mengembalikan respons 200 dengan tajuk dan isi tertentu.

Bahagian kedua kontrak kami adalah kaedah POST. Apabila pelanggan menghantar permintaan POST ke jalan / perjanjian dengan badan JSON yang betul, ia mengembalikan kod respons HTTP 201.

Mari tentukan kontrak tersebut dengan Pact:

(...) .given("test POST") .uponReceiving("POST REQUEST") .method("POST") .headers(headers) .body("{\"name\": \"Michael\"}") .path("/pact") .willRespondWith() .status(201) .toPact();

Perhatikan bahawa kita perlu memanggil kaedah toPact () pada akhir kontrak untuk mengembalikan contoh RequestResponsePact .

4.1. Hasil Artifak Pakatan

Secara lalai, fail Perjanjian akan dihasilkan dalam folder sasaran / perjanjian . Untuk menyesuaikan jalan ini, kami dapat mengkonfigurasi plugin maven-surefire:

 org.apache.maven.plugins maven-surefire-plugin   target/mypacts   ... 

Pembinaan Maven akan menghasilkan fail yang disebut test_consumer-test_provider.json dalam folder target / mypacts yang mengandungi struktur permintaan dan respons:

{ "provider": { "name": "test_provider" }, "consumer": { "name": "test_consumer" }, "interactions": [ { "description": "GET REQUEST", "request": { "method": "GET", "path": "/" }, "response": { "status": 200, "headers": { "Content-Type": "application/json" }, "body": { "condition": true, "name": "tom" } }, "providerStates": [ { "name": "test GET" } ] }, { "description": "POST REQUEST", ... } ], "metadata": { "pact-specification": { "version": "3.0.0" }, "pact-jvm": { "version": "3.5.0" } } }

5. Menguji Pelanggan dan Penyedia Menggunakan Kontrak

Sekarang kita mempunyai kontrak, kita boleh gunakan untuk membuat ujian terhadap pelanggan dan pembekal.

Setiap ujian ini akan menggunakan ejekan rakan sejawatannya yang berdasarkan kontrak, yang bermaksud:

  • pelanggan akan menggunakan penyedia tiruan
  • pembekal akan menggunakan pelanggan tiruan

Berkesan, ujian dilakukan terhadap kontrak.

5.1. Menguji Pelanggan

Setelah kami menentukan kontrak, kami dapat menguji interaksi dengan perkhidmatan yang akan dibuat berdasarkan kontrak tersebut. Kita boleh membuat ujian JUnit biasa tetapi kita perlu ingat untuk meletakkan anotasi @PactVerification pada awal ujian.

Mari tulis ujian untuk permintaan GET:

@Test @PactVerification() public void givenGet_whenSendRequest_shouldReturn200WithProperHeaderAndBody() { // when ResponseEntity response = new RestTemplate() .getForEntity(mockProvider.getUrl() + "/pact", String.class); // then assertThat(response.getStatusCode().value()).isEqualTo(200); assertThat(response.getHeaders().get("Content-Type").contains("application/json")).isTrue(); assertThat(response.getBody()).contains("condition", "true", "name", "tom"); }

The @PactVerification anotasi menjaga memulakan perkhidmatan HTTP. Dalam ujian, kami hanya perlu menghantar permintaan GET dan menegaskan bahawa respons kami mematuhi kontrak.

Mari tambah ujian untuk panggilan kaedah POST juga:

HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); String jsonBody = "{\"name\": \"Michael\"}"; // when ResponseEntity postResponse = new RestTemplate() .exchange( mockProvider.getUrl() + "/create", HttpMethod.POST, new HttpEntity(jsonBody, httpHeaders), String.class ); //then assertThat(postResponse.getStatusCode().value()).isEqualTo(201);

Seperti yang kita lihat, kod respons untuk permintaan POST adalah sama dengan 201 - persis seperti yang ditentukan dalam kontrak Perjanjian .

As we were using the @PactVerification() annotation, the Pact library is starting the web server based on the previously defined contract before our test case.

5.2. Testing the Provider

The second step of our contract verification is creating a test for the provider using a mock client based on the contract.

Our provider implementation will be driven by this contract in TDD fashion.

For our example, we'll use a Spring Boot REST API.

First, to create our JUnit test, we'll need to add the pact-jvm-provider-junit_2.11 dependency:

 au.com.dius pact-jvm-provider-junit_2.11 3.5.0 test 

This allows us to create a JUnit test using the PactRunner and specifying the provider name and the location of the Pact artifact:

@RunWith(PactRunner.class) @Provider("test_provider") @PactFolder("pacts") public class PactProviderTest { //... }

For this configuration to work, we have to place the test_consumer-test_provider.json file in the pacts folder of our REST service project.

Next, we'll define the target to be used for verifying the interactions in the contract and start up the Spring Boot app before running the tests:

@TestTarget public final Target target = new HttpTarget("http", "localhost", 8082, "/spring-rest"); private static ConfigurableWebApplicationContext application; @BeforeClass public static void start() { application = (ConfigurableWebApplicationContext) SpringApplication.run(MainApplication.class); }

Finally, we'll specify the states in the contract that we want to test:

@State("test GET") public void toGetState() { } @State("test POST") public void toPostState() { }

Running this JUnit class will execute two tests for the two GET and POST requests. Let's take a look at the log:

Verifying a pact between test_consumer and test_provider Given test GET GET REQUEST returns a response which has status code 200 (OK) includes headers "Content-Type" with value "application/json" (OK) has a matching body (OK) Verifying a pact between test_consumer and test_provider Given test POST POST REQUEST returns a response which has status code 201 (OK) has a matching body (OK)

Note that we haven't included the code for creating a REST service here. The full service and test can be found in the GitHub project.

6. Conclusion

In this quick tutorial, we had a look at Consumer Driven Contracts.

Kami membuat kontrak menggunakan perpustakaan Perjanjian . Setelah kami menentukan kontrak, kami dapat menguji pelanggan dan perkhidmatan terhadap kontrak dan menegaskan bahawa mereka mematuhi spesifikasi.

Pelaksanaan semua contoh dan coretan kod ini terdapat dalam projek GitHub - ini adalah projek Maven, jadi mudah untuk diimport dan dijalankan sebagaimana adanya.