Uji API REST dengan Timun

1. Gambaran keseluruhan

Tutorial ini memberikan pengenalan kepada Timun, alat yang biasa digunakan untuk ujian penerimaan pengguna, dan cara menggunakannya dalam ujian API REST.

Sebagai tambahan, untuk menjadikan artikel itu mandiri dan tidak bergantung kepada perkhidmatan REST luaran, kami akan menggunakan WireMock, perpustakaan perkhidmatan web yang mencemaskan dan mengejek. Sekiranya anda ingin mengetahui lebih lanjut mengenai perpustakaan ini, sila rujuk pengenalan kepada WireMock.

2. Gherkin - Bahasa Timun

Timun adalah kerangka pengujian yang menyokong Behavior Driven Development (BDD), yang memungkinkan pengguna untuk menentukan operasi aplikasi dalam teks biasa. Ia berfungsi berdasarkan Bahasa Spesifik Domain Gherkin (DSL). Sintaks Gherkin yang mudah tetapi kuat ini membolehkan pemaju dan penguji menulis ujian yang rumit sambil terus difahami oleh pengguna bukan teknikal.

2.1. Pengenalan kepada Gherkin

Gherkin adalah bahasa berorientasikan garis yang menggunakan hujung baris, lekukan dan kata kunci untuk menentukan dokumen. Setiap baris yang tidak kosong biasanya dimulakan dengan kata kunci Gherkin, diikuti oleh teks sewenang-wenangnya, yang biasanya merupakan keterangan tentang kata kunci.

Keseluruhan struktur mesti ditulis ke dalam fail dengan peluasan ciri untuk dikenali oleh Cucumber.

Berikut adalah contoh dokumen Gherkin yang mudah:

Feature: A short description of the desired functionality Scenario: A business situation Given a precondition And another precondition When an event happens And another event happens too Then a testable outcome is achieved And something else is also completed

Dalam bahagian berikut, kami akan menerangkan beberapa elemen terpenting dalam struktur Gherkin.

2.2. Ciri

Kami menggunakan fail Gherkin untuk menggambarkan ciri aplikasi yang perlu diuji. Fail mengandungi kata kunci Feature pada awalnya, diikuti oleh nama ciri pada baris yang sama dan penerangan pilihan yang mungkin merangkumi beberapa baris di bawahnya.

Pengurai timun melangkau semua teks, kecuali kata kunci Ciri , dan menyertakannya untuk tujuan dokumentasi sahaja.

2.3. Senario dan Langkah

Struktur Gherkin mungkin terdiri daripada satu atau lebih senario, yang dikenali oleh kata kunci Skenario . Senario pada dasarnya adalah ujian yang membolehkan pengguna mengesahkan keupayaan aplikasi. Ia harus menggambarkan konteks awal, peristiwa yang mungkin berlaku dan hasil yang diharapkan dibuat oleh peristiwa tersebut.

Perkara-perkara ini dilakukan dengan menggunakan langkah-langkah, pasti dengan salah satu daripada lima kata kunci: Memandangkan , Apabila , Kemudian , Dan , dan Tetapi .

  • Diberikan : Langkah ini adalah untuk meletakkan sistem ke keadaan yang jelas sebelum pengguna mula berinteraksi dengan aplikasi. A Memandangkan fasal boleh dianggap sebagai pra-syarat untuk kes penggunaan.
  • When : A When step digunakan untuk menggambarkan peristiwa yang berlaku pada aplikasi. Ini boleh menjadi tindakan yang diambil oleh pengguna, atau peristiwa yang dipicu oleh sistem lain.
  • Kemudian : Langkah ini adalah untuk menentukan hasil ujian yang diharapkan. Hasilnya harus berkaitan dengan nilai perniagaan ciri yang diuji.
  • Dan dan Tetapi : Kata kunci ini boleh digunakan untuk menggantikan kata kunci langkah di atas apabila terdapat beberapa langkah dengan jenis yang sama.

Timun sebenarnya tidak membezakan kata kunci ini, namun kata kunci tersebut masih ada untuk menjadikan ciri tersebut lebih mudah dibaca dan selaras dengan struktur BDD.

3. Pelaksanaan Timun-JVM

Timun pada mulanya ditulis dalam Ruby dan telah dibawa ke Java dengan implementasi Cucumber-JVM, yang merupakan subjek dari bahagian ini.

3.1. Ketergantungan Maven

Untuk memanfaatkan Cucumber-JVM dalam projek Maven, kebergantungan berikut perlu disertakan dalam POM:

 io.cucumber cucumber-java 6.8.0 test 

Untuk memudahkan ujian JUnit dengan Timun, kita perlu mempunyai satu lagi kebergantungan:

 io.cucumber cucumber-junit 6.8.0 

Sebagai alternatif, kita dapat menggunakan artifak lain untuk memanfaatkan ungkapan lambda di Java 8, yang tidak akan dibahas dalam tutorial ini.

3.2. Definisi Langkah

Senario Gherkin tidak akan berguna sekiranya tidak diterjemahkan ke dalam tindakan dan di sinilah definisi langkah dimainkan. Pada dasarnya, definisi langkah adalah kaedah Java yang dianotasi dengan corak terlampir yang tugasnya adalah untuk menukar langkah-langkah Gherkin dalam teks biasa menjadi kod yang dapat dilaksanakan. Setelah menguraikan dokumen ciri, Timun akan mencari definisi langkah yang sesuai dengan langkah-langkah Gherkin yang telah ditentukan untuk dilaksanakan.

Untuk membuatnya lebih jelas, mari kita lihat langkah berikut:

Given I have registered a course in Baeldung

Dan definisi langkah:

@Given("I have registered a course in Baeldung") public void verifyAccount() { // method implementation }

Apabila Timun membaca langkah yang diberikan, ia akan mencari definisi langkah yang corak penjelasannya sesuai dengan teks Gherkin.

4. Membuat dan Menjalankan Ujian

4.1. Menulis Fail Ciri

Mari kita mulakan dengan menyatakan senario dan langkah dalam fail dengan nama yang berakhir dengan pelanjutan .feature :

Feature: Testing a REST API Users should be able to submit GET and POST requests to a web service, represented by WireMock Scenario: Data Upload to a web service When users upload data on a project Then the server should handle it and return a success status Scenario: Data retrieval from a web service When users want to get information on the 'Cucumber' project Then the requested data is returned

Kami sekarang menyimpan fail ini dalam direktori bernama Feature , dengan syarat direktori akan dimuat ke classpath pada waktu runtime, misalnya src / main / resources .

4.2. Mengkonfigurasi JUnit untuk Bekerja Dengan Timun

Agar JUnit mengetahui tentang Cucumber dan membaca fail ciri semasa berjalan, kelas Cucumber mesti dinyatakan sebagai Runner . Kita juga perlu memberitahu JUnit tempat untuk mencari fail ciri dan definisi langkah.

@RunWith(Cucumber.class) @CucumberOptions(features = "classpath:Feature") public class CucumberIntegrationTest { }

Seperti yang anda lihat, elemen ciri CucumberOption mencari fail ciri yang dibuat sebelumnya. Unsur penting lain, yang disebut gam , memberikan jalan ke definisi langkah. Walau bagaimanapun, jika kes ujian dan definisi langkah dalam pakej yang sama seperti dalam tutorial ini, elemen itu mungkin akan dijatuhkan.

4.3. Definisi Langkah Menulis

Apabila Timun menguraikan langkah-langkah, ia akan mencari kaedah yang dianotasikan dengan kata kunci Gherkin untuk mencari definisi langkah yang sepadan.

Ungkapan definisi langkah boleh berupa Ungkapan Biasa atau Ekspresi Timun. Dalam tutorial ini, kita akan menggunakan Cucumber Expressions.

Berikut adalah kaedah yang sesuai dengan langkah Gherkin. Kaedah ini akan digunakan untuk mengirim data ke perkhidmatan web REST:

@When("users upload data on a project") public void usersUploadDataOnAProject() throws IOException { }

Dan berikut adalah kaedah yang sesuai dengan langkah Gherkin dan mengambil argumen dari teks, yang akan digunakan untuk mendapatkan maklumat dari perkhidmatan web REST:

@When("users want to get information on the {string} project") public void usersGetInformationOnAProject(String projectName) throws IOException { }

Seperti yang anda lihat, kaedah usersGetInformationOnAProject mengambil argumen String , yang merupakan nama projek. Argumen ini dinyatakan oleh {string} dalam anotasi dan di sini sesuai dengan Timun dalam teks langkah.

Sebagai alternatif, kita boleh menggunakan ungkapan biasa:

@When("^users want to get information on the '(.+)' project$") public void usersGetInformationOnAProject(String projectName) throws IOException { }

Note, the ‘^' and ‘$' which indicate the start and end of the regex accordingly. Whereas ‘(.+)' corresponds to the String parameter.

We'll provide the working code for both of the above methods in the next section.

4.4. Creating and Running Tests

First, we will begin with a JSON structure to illustrate the data uploaded to the server by a POST request, and downloaded to the client using a GET. This structure is saved in the jsonString field, and shown below:

{ "testing-framework": "cucumber", "supported-language": [ "Ruby", "Java", "Javascript", "PHP", "Python", "C++" ], "website": "cucumber.io" }

To demonstrate a REST API, we use a WireMock server:

WireMockServer wireMockServer = new WireMockServer(options().dynamicPort());

In addition, we'll use Apache HttpClient API to represent the client used to connect to the server:

CloseableHttpClient httpClient = HttpClients.createDefault();

Now, let's move on to writing testing code within step definitions. We will do this for the usersUploadDataOnAProject method first.

The server should be running before the client connects to it:

wireMockServer.start();

Using the WireMock API to stub the REST service:

configureFor("localhost", wireMockServer.port()); stubFor(post(urlEqualTo("/create")) .withHeader("content-type", equalTo("application/json")) .withRequestBody(containing("testing-framework")) .willReturn(aResponse().withStatus(200)));

Now, send a POST request with the content taken from the jsonString field declared above to the server:

HttpPost request = new HttpPost("//localhost:" + wireMockServer.port() + "/create"); StringEntity entity = new StringEntity(jsonString); request.addHeader("content-type", "application/json"); request.setEntity(entity); HttpResponse response = httpClient.execute(request);

The following code asserts that the POST request has been successfully received and handled:

assertEquals(200, response.getStatusLine().getStatusCode()); verify(postRequestedFor(urlEqualTo("/create")) .withHeader("content-type", equalTo("application/json")));

The server should stop after being used:

wireMockServer.stop();

The second method we will implement herein is usersGetInformationOnAProject(String projectName ). Similar to the first test, we need to start the server and then stub the REST service:

wireMockServer.start(); configureFor("localhost", wireMockServer.port()); stubFor(get(urlEqualTo("/projects/cucumber")) .withHeader("accept", equalTo("application/json")) .willReturn(aResponse().withBody(jsonString)));

Submitting a GET request and receiving a response:

HttpGet request = new HttpGet("//localhost:" + wireMockServer.port() + "/projects/" + projectName.toLowerCase()); request.addHeader("accept", "application/json"); HttpResponse httpResponse = httpClient.execute(request);

We will convert the httpResponse variable to a String using a helper method:

String responseString = convertResponseToString(httpResponse);

Here is the implementation of that conversion helper method:

private String convertResponseToString(HttpResponse response) throws IOException { InputStream responseStream = response.getEntity().getContent(); Scanner scanner = new Scanner(responseStream, "UTF-8"); String responseString = scanner.useDelimiter("\\Z").next(); scanner.close(); return responseString; }

The following verifies the whole process:

assertThat(responseString, containsString("\"testing-framework\": \"cucumber\"")); assertThat(responseString, containsString("\"website\": \"cucumber.io\"")); verify(getRequestedFor(urlEqualTo("/projects/cucumber")) .withHeader("accept", equalTo("application/json")));

Finally, stop the server as described before.

5. Running Features in Parallel

Cucumber-JVM natively supports parallel test execution across multiple threads. We'll use JUnit together with Maven Failsafe plugin to execute the runners. Alternatively, we could use Maven Surefire.

JUnit runs the feature files in parallel rather than scenarios, which means all the scenarios in a feature file will be executed by the same thread.

Let's now add the plugin configuration:

 maven-failsafe-plugin ${maven-failsafe-plugin.version}   CucumberIntegrationTest.java  methods 2     integration-test verify    

Note that:

  • selari: boleh menjadi kelas, kaedah , atau kedua-duanya - dalam kes kami, kelas akan menjadikan setiap kelas ujian dijalankan dalam utas yang berasingan
  • threadCount: menunjukkan berapa banyak utas yang harus diperuntukkan untuk pelaksanaan ini

Itu sahaja yang perlu kita lakukan untuk menjalankan ciri-ciri Timun secara selari.

6. Kesimpulannya

Dalam tutorial ini, kami membahas asas-asas Cucumber dan bagaimana kerangka ini menggunakan bahasa khusus domain Gherkin untuk menguji REST API.

Seperti biasa, semua sampel kod yang ditunjukkan dalam tutorial ini terdapat di GitHub.