Cucumberを使用したRESTAPIテスト
1. 概要
このチュートリアルでは、ユーザー受け入れテストで一般的に使用されるツールである Cucumber と、RESTAPIテストでの使用方法について説明します。
さらに、記事を自己完結型にし、外部のRESTサービスから独立させるために、スタブおよびモッキングのWebサービスライブラリであるWireMockを使用します。 このライブラリについて詳しく知りたい場合は、WireMockの概要を参照してください。
2. ガーキン–きゅうりの言語
Cucumberは、 Behavior Driven Development(BDD)をサポートするテストフレームワークであり、ユーザーがアプリケーション操作をプレーンテキストで定義できるようにします。 Gherkinドメイン固有言語(DSL)に基づいて動作します。 このシンプルで強力なGherkinの構文により、開発者とテスターは、技術者でないユーザーでも理解できるようにしながら、複雑なテストを作成できます。
2.1. ガーキン入門
Gherkinは、行の終わり、インデント、およびキーワードを使用してドキュメントを定義する行指向の言語です。 空白以外の各行は通常、Gherkinキーワードで始まり、その後に任意のテキストが続きます。これは通常、キーワードの説明です。
Cucumberで認識されるようにするには、構造全体をfeature拡張子の付いたファイルに書き込む必要があります。
簡単な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. シナリオと手順
ガーキン構造は、シナリオキーワードによって認識される1つ以上のシナリオで構成されている場合があります。 シナリオは基本的に、ユーザーがアプリケーションの機能を検証できるようにするテストです。 初期コンテキスト、発生する可能性のあるイベント、およびそれらのイベントによって作成される予想される結果を説明する必要があります。
これらのことは、 Given 、 When 、 Then 、 And 、およびの5つのキーワードのいずれかで識別されるステップを使用して行われます。 ]しかし。
- Given :このステップは、ユーザーがアプリケーションとの対話を開始する前に、システムを明確に定義された状態にすることです。 Given 句は、ユースケースの前提条件と見なすことができます。
- When : When ステップは、アプリケーションで発生するイベントを記述するために使用されます。 これは、ユーザーが実行したアクション、または別のシステムによってトリガーされたイベントである可能性があります。
- Then :このステップは、テストの期待される結果を指定することです。 結果は、テスト対象の機能のビジネス価値に関連している必要があります。
- And and But :これらのキーワードは、同じタイプのステップが複数ある場合に、上記のステップキーワードを置き換えるために使用できます。
Cucumberは実際にはこれらのキーワードを区別しませんが、機能をより読みやすくし、BDD構造と一致させるために引き続き存在します。
3. キュウリ-JVMの実装
Cucumberは元々Rubyで作成され、このセクションの主題であるCucumber-JVM実装を使用してJavaに移植されました。
3.1. Mavenの依存関係
MavenプロジェクトでCucumber-JVMを利用するには、次の依存関係をPOMに含める必要があります。
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>6.8.0</version>
<scope>test</scope>
</dependency>
Cucumberを使用したJUnitテストを容易にするには、もう1つの依存関係が必要です。
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>6.8.0</version>
</dependency>
または、別のアーティファクトを使用して、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 {
}
ご覧のとおり、CucumberOptionのfeatures要素は、以前に作成された機能ファイルを検索します。 glue と呼ばれる別の重要な要素は、ステップ定義へのパスを提供します。 ただし、テストケースとステップの定義がこのチュートリアルと同じパッケージに含まれている場合、その要素は削除される可能性があります。
4.3. ステップ定義の記述
Cucumberがステップを解析するとき、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 {
}
‘^’および‘$’は、それに応じて正規表現の開始と終了を示していることに注意してください。 一方、‘(。+)’はStringパラメーターに対応します。
次のセクションでは、上記の両方のメソッドの作業コードを提供します。
4.4. テストの作成と実行
まず、JSON構造から始めて、POSTリクエストによってサーバーにアップロードされ、GETを使用してクライアントにダウンロードされるデータを示します。 この構造はjsonStringフィールドに保存され、以下に示されています。
{
"testing-framework": "cucumber",
"supported-language":
[
"Ruby",
"Java",
"Javascript",
"PHP",
"Python",
"C++"
],
"website": "cucumber.io"
}
REST APIを示すために、WireMockサーバーを使用します。
WireMockServer wireMockServer = new WireMockServer(options().dynamicPort());
さらに、Apache HttpClient APIを使用して、サーバーへの接続に使用されるクライアントを表します。
CloseableHttpClient httpClient = HttpClients.createDefault();
それでは、ステップ定義内でのテストコードの記述に移りましょう。 最初にusersUploadDataOnAProjectメソッドに対してこれを行います。
クライアントがサーバーに接続する前に、サーバーが実行されている必要があります。
wireMockServer.start();
WireMock APIを使用してRESTサービスをスタブします:
configureFor("localhost", wireMockServer.port());
stubFor(post(urlEqualTo("/create"))
.withHeader("content-type", equalTo("application/json"))
.withRequestBody(containing("testing-framework"))
.willReturn(aResponse().withStatus(200)));
次に、上記で宣言されたjsonStringフィールドから取得したコンテンツを含むPOSTリクエストをサーバーに送信します。
HttpPost request = new HttpPost("http://localhost:" + wireMockServer.port() + "/create");
StringEntity entity = new StringEntity(jsonString);
request.addHeader("content-type", "application/json");
request.setEntity(entity);
HttpResponse response = httpClient.execute(request);
次のコードは、POSTリクエストが正常に受信されて処理されたことを示しています。
assertEquals(200, response.getStatusLine().getStatusCode());
verify(postRequestedFor(urlEqualTo("/create"))
.withHeader("content-type", equalTo("application/json")));
サーバーは使用後に停止する必要があります。
wireMockServer.stop();
ここで実装する2番目のメソッドは、 usersGetInformationOnAProject(String projectName)です。 最初のテストと同様に、サーバーを起動してから、RESTサービスをスタブする必要があります。
wireMockServer.start();
configureFor("localhost", wireMockServer.port());
stubFor(get(urlEqualTo("/projects/cucumber"))
.withHeader("accept", equalTo("application/json"))
.willReturn(aResponse().withBody(jsonString)));
GETリクエストの送信とレスポンスの受信:
HttpGet request = new HttpGet("http://localhost:" + wireMockServer.port() + "/projects/" + projectName.toLowerCase());
request.addHeader("accept", "application/json");
HttpResponse httpResponse = httpClient.execute(request);
ヘルパーメソッドを使用して、httpResponse変数をStringに変換します。
String responseString = convertResponseToString(httpResponse);
その変換ヘルパーメソッドの実装は次のとおりです。
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;
}
以下は、プロセス全体を検証します。
assertThat(responseString, containsString("\"testing-framework\": \"cucumber\""));
assertThat(responseString, containsString("\"website\": \"cucumber.io\""));
verify(getRequestedFor(urlEqualTo("/projects/cucumber"))
.withHeader("accept", equalTo("application/json")));
最後に、前述のようにサーバーを停止します。
5. 機能を並行して実行する
Cucumber-JVMは、複数のスレッドにわたる並列テスト実行をネイティブにサポートします。 JUnitをMavenFailsafeプラグインと一緒に使用して、ランナーを実行します。 または、MavenSurefireを使用することもできます。
JUnitは、シナリオではなく並列で機能ファイルを実行します。つまり、機能ファイル内のすべてのシナリオは同じスレッドによって実行されます。
プラグイン構成を追加しましょう:
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
<configuration>
<includes>
<include>CucumberIntegrationTest.java</include>
</includes>
<parallel>methods</parallel>
<threadCount>2</threadCount>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
ご了承ください:
- parallel:は、クラス、メソッド、またはその両方にすることができます。この場合、 classes は、各テストクラスを個別のスレッドで実行します。
- threadCount:は、この実行に割り当てるスレッドの数を示します
Cucumber機能を並行して実行するために必要なのはこれだけです。
6. 結論
このチュートリアルでは、Cucumberの基本と、このフレームワークがRESTAPIをテストするためにGherkinドメイン固有言語をどのように使用するかについて説明しました。
いつものように、このチュートリアルに示されているすべてのコードサンプルは、GitHubでから入手できます。