Cucumberを使用したRESTAPIテスト

1。概要

このチュートリアルでは、ユーザー受け入れテストで一般的に使用されるツールであるCucumberの概要と、RESTAPIテストでの使用方法について説明します。

さらに、記事を自己完結型にし、外部のRESTサービスから独立させるために、スタブおよびモックのWebサービスライブラリであるWireMockを使用します。このライブラリについて詳しく知りたい場合は、WireMockの概要を参照してください。

2.ガーキン–キュウリの言語

Cucumberは、ビヘイビア駆動開発(BDD)をサポートするテストフレームワークであり、ユーザーがアプリケーション操作をプレーンテキストで定義できるようにします。Gherkinドメイン固有言語(DSL)に基づいて機能します。Gherkinのこのシンプルで強力な構文により、開発者とテスターは、技術者でないユーザーでも理解できるようにしながら、複雑なテストを作成できます。

2.1。ガーキン入門

Gherkinは、行末、インデント、およびキーワードを使用してドキュメントを定義する行指向言語です。空白以外の各行は通常、Gherkinキーワードで始まり、その後に任意のテキストが続きます。これは通常、キーワードの説明です。

Cucumberが認識できるように、構造全体を機能拡張子付きのファイルに書き込む必要があります。

簡単なGherkinドキュメントの例を次に示します。

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

次のセクションでは、ガーキン構造の最も重要な要素をいくつか説明します。

2.2。特徴

Gherkinファイルを使用して、テストが必要なアプリケーション機能を記述します。このファイルには、最初にFeatureキーワードが含まれ、その後に同じ行に機能名が続き、その下に複数行にまたがるオプションの説明が続きます。

Cucumberパーサーは、Featureキーワードを除くすべてのテキストをスキップし、ドキュメント化の目的でのみ含めます。

2.3。シナリオと手順

Gherkin構造は、Scenarioキーワードによって認識される1つ以上のシナリオで構成されている場合があります。シナリオは基本的に、ユーザーがアプリケーションの機能を検証できるようにするテストです。初期コンテキスト、発生する可能性のあるイベント、およびそれらのイベントによって作成される期待される結果について説明する必要があります。

これらのことは、GivenWhenThenAndButの5つの​​キーワードのいずれかで識別されるステップを使用して行われます。

  • 与えられた:このステップは、ユーザーがアプリケーションとの対話を開始する前に、システムを明確に定義された状態にすることです。与えられた句はによってユースケースのための前提条件と考えることができます。
  • いつ:Aいつステップを使用して、アプリケーションに発生するイベントを記述します。これは、ユーザーが実行したアクション、または別のシステムによってトリガーされたイベントの場合があります。
  • 次に:このステップは、テストの期待される結果を指定することです。結果は、テスト対象の機能のビジネス価値に関連している必要があります。
  • And and But:これらのキーワードは、同じタイプのステップが複数ある場合に、上記のステップキーワードを置き換えるために使用できます。

Cucumberは実際にはこれらのキーワードを区別しませんが、機能をより読みやすくし、BDD構造と整合させるために存在します。

3.キュウリ-JVMの実装

Cucumberは元々Rubyで作成され、このセクションの主題であるCucumber-JVM実装を使用してJavaに移植されました。

3.1。Mavenの依存関係

MavenプロジェクトでCucumber-JVMを利用するには、次の依存関係をPOMに含める必要があります。

 io.cucumber cucumber-java 6.8.0 test 

Cucumberを使用したJUnitテストを容易にするには、もう1つの依存関係が必要です。

 io.cucumber cucumber-junit 6.8.0 

または、別のアーティファクトを使用して、Java 8のラムダ式を利用することもできますが、このチュートリアルでは取り上げません。

3.2。ステップの定義

ガーキンのシナリオは、アクションに変換されなければ役に立たないでしょう。ここでステップ定義が役立ちます。基本的に、ステップ定義は、プレーンテキストのGherkinステップを実行可能コードに変換することを目的としたパターンが付加された注釈付きJavaメソッドです。機能ドキュメントを解析した後、Cucumberは、実行する事前定義されたGherkinステップに一致するステップ定義を検索します。

わかりやすくするために、次の手順を見てみましょう。

Given I have registered a course in Baeldung

そしてステップの定義:

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

Cucumberが指定されたステップを読み取ると、注釈パターンがGherkinテキストと一致するステップ定義が検索されます。

4.テストの作成と実行

4.1。機能ファイルの作成

名前が.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

ここで、このファイルをFeatureという名前のディレクトリに保存します。ただし、実行時にディレクトリがクラスパスにロードされることを条件とします(例:src / main / resources)

4.2。Cucumberと連携するようにJUnitを構成する

JUnitが実行時にCucumberを認識し、機能ファイルを読み取るためには、CucumberクラスをRunnerとして宣言する必要があります。また、機能ファイルとステップ定義を検索する場所をJUnitに指示する必要があります。

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

ご覧のとおり、CucumberOptionfeatures要素は、以前に作成された機能ファイルを検索します。接着剤と呼ばれるもう1つの重要な要素は、ステップ定義へのパスを提供します。ただし、テストケースとステップの定義がこのチュートリアルと同じパッケージに含まれている場合、その要素は削除される可能性があります。

4.3。ステップ定義の記述

Cucumberがステップを解析するとき、Gherkinキーワードで注釈が付けられたメソッドを検索して、一致するステップ定義を見つけます。

ステップ定義の式は、正規表現またはキュウリ式のいずれかです。このチュートリアルでは、キュウリ式を使用します。

以下は、Gherkinステップに完全に一致する方法です。このメソッドは、RESTWebサービスにデータを投稿するために使用されます。

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

そして、これはGherkinステップに一致するメソッドであり、テキストから引数を取ります。これは、RESTWebサービスから情報を取得するために使用されます。

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

ご覧のとおり、usersGetInformationOnAProjectメソッドは、プロジェクト名であるString引数を取ります。この引数は、注釈の{string}によって宣言されており、ここでは、ステップテキストのCucumberに対応しています。

または、正規表現を使用することもできます。

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

  • 並列:クラス、メソッド、またはその両方にすることができます–この場合、クラスは各テストクラスを別々のスレッドで実行します
  • threadCount:この実行に割り当てるスレッドの数を示します

Cucumber機能を並行して実行するために必要なのはこれだけです。

6.結論

このチュートリアルでは、Cucumberの基本と、このフレームワークがGherkinドメイン固有言語を使用してRESTAPIをテストする方法について説明しました。

いつものように、このチュートリアルに示されているすべてのコードサンプルはGitHubから入手できます。