JBehaveによるREST APIのテスト

JBehaveを使用したREST APIのテスト

1.はじめに

この記事では、JBehaveを簡単に見てから、BDDの観点からRESTAPIをテストすることに焦点を当てます。

2. JBehaveおよびBDD

JBehaveは、行動駆動型開発フレームワークです。 自動受け入れテストのための直感的でアクセス可能な方法を提供する予定です。

BDDに慣れていない場合は、this article, covering on another BDD testing framework – Cucumberから始めることをお勧めします。ここでは、一般的なBDDの構造と機能を紹介しています。

他のBDDフレームワークと同様に、JBehaveは次の概念を採用しています。

  • ストーリー-自動的に実行可能なビジネス機能の増分を表し、1つ以上のシナリオで構成されます

  • シナリオ–システムの動作の具体的な例を表します

  • 手順–従来のBDDキーワード(GivenWhenThen)を使用して実際の動作を表します

典型的なシナリオは次のとおりです。

Given a precondition
When an event occurs
Then the outcome should be captured

シナリオの各ステップは、JBehaveの注釈に対応しています。

  • @Given:コンテキストを開始します。

  • @When:アクションを実行する

  • @Then:期待される結果をテストします

3. メーベン依存

MavenプロジェクトでJBehaveを利用するには、jbehave-core依存関係をpomに含める必要があります。


    org.jbehave
    jbehave-core
    4.1
    test

4. 簡単な例

JBehaveを使用するには、次の手順に従う必要があります。

  1. ユーザーストーリーを書く

  2. ユーザーストーリーからJavaコードへのステップのマッピング

  3. ユーザーストーリーを構成する

  4. JBehaveテストを実行する

  5. 結果を確認する

4.1. ストーリー

次の簡単な話から始めましょう:「ユーザーとして、カウンターの値を1増やすことができるように、カウンターを増やしたい」。

ストーリーは.storyファイルで定義できます。

Scenario: when a user increases a counter, its value is increased by 1

Given a counter
And the counter has any integral value
When the user increases the counter
Then the value of the counter must be 1 greater than previous value

4.2. マッピング手順

手順を踏まえて、これをJavaで実装しましょう。

public class IncreaseSteps {
    private int counter;
    private int previousValue;

    @Given("a counter")
    public void aCounter() {
    }

    @Given("the counter has any integral value")
    public void counterHasAnyIntegralValue() {
        counter = new Random().nextInt();
        previousValue = counter;
    }

    @When("the user increases the counter")
    public void increasesTheCounter() {
        counter++;
    }

    @Then("the value of the counter must be 1 greater than previous value")
    public void theValueOfTheCounterMustBe1Greater() {
        assertTrue(1 == counter - previousValue);
    }
}

the value in the annotations must accurately match the descriptionであることを忘れないでください。

4.3. ストーリーの構成

手順を実行するには、ストーリーのステージを設定する必要があります。

public class IncreaseStoryLiveTest extends JUnitStories {

    @Override
    public Configuration configuration() {
        return new MostUsefulConfiguration()
          .useStoryLoader(new LoadFromClasspath(this.getClass()))
          .useStoryReporterBuilder(new StoryReporterBuilder()
            .withCodeLocation(codeLocationFromClass(this.getClass()))
            .withFormats(CONSOLE));
    }

    @Override
    public InjectableStepsFactory stepsFactory() {
        return new InstanceStepsFactory(configuration(), new IncreaseSteps());
    }

    @Override
    protected List storyPaths() {
        return Arrays.asList("increase.story");
    }

}

storyPaths()には、JBehaveによって解析される.storyファイルパスを指定します。 実際のステップの実装はstepsFactory()で提供されます。 次に、configuration()で、ストーリーローダーとストーリーレポートが適切に構成されます。

すべての準備が整ったので、mvn clean testを実行するだけでストーリーを開始できます。

4.4. テスト結果の確認

コンソールでテスト結果を確認できます。 テストが成功したため、出力はストーリーと同じになります。

Scenario: when a user increases a counter, its value is increased by 1
Given a counter
And the counter has any integral value
When the user increases the counter
Then the value of the counter must be 1 greater than previous value

シナリオのステップを実装することを忘れた場合、レポートは私達に知らせます。 @Whenステップを実装しなかったとしましょう。

Scenario: when a user increases a counter, its value is increased by 1
Given a counter
And the counter has any integral value
When the user increases the counter (PENDING)
Then the value of the counter must be 1 greater than previous value (NOT PERFORMED)
@When("the user increases the counter")
@Pending
public void whenTheUserIncreasesTheCounter() {
    // PENDING
}

レポートには、@Whenステップが保留中であると記載されているため、@Thenステップは実行されません。

@Thenステップが失敗した場合はどうなりますか? レポートからすぐにエラーを見つけることができます。

Scenario: when a user increases a counter, its value is increased by 1
Given a counter
And the counter has any integral value
When the user increases the counter
Then the value of the counter must be 1 greater than previous value (FAILED)
(java.lang.AssertionError)

5. REST APIのテスト

これで、JBhaveの基本を理解できました。それを使用してRESTAPIをテストする方法を見ていきます。 テストはprevious article discussing how to test REST API with Javaに基づいて行われます。

その記事では、GitHub REST APIをテストし、主にHTTP応答コード、ヘッダー、およびペイロードに焦点を当てました。 簡単にするために、それらをそれぞれ3つの別々のストーリーに書くことができます。

5.1. ステータスコードのテスト

物語:

Scenario: when a user checks a non-existent user on github, github would respond 'not found'

Given github user profile api
And a random non-existent username
When I look for the random user via the api
Then github respond: 404 not found

When I look for eugenp1 via the api
Then github respond: 404 not found

When I look for eugenp2 via the api
Then github respond: 404 not found

ステップ:

public class GithubUserNotFoundSteps {

    private String api;
    private String nonExistentUser;
    private int githubResponseCode;

    @Given("github user profile api")
    public void givenGithubUserProfileApi() {
        api = "https://api.github.com/users/%s";
    }

    @Given("a random non-existent username")
    public void givenANonexistentUsername() {
        nonExistentUser = randomAlphabetic(8);
    }

    @When("I look for the random user via the api")
    public void whenILookForTheUserViaTheApi() throws IOException {
        githubResponseCode = getGithubUserProfile(api, nonExistentUser)
          .getStatusLine()
          .getStatusCode();
    }

    @When("I look for $user via the api")
    public void whenILookForSomeNonExistentUserViaTheApi(
      String user) throws IOException {
        githubResponseCode = getGithubUserProfile(api, user)
          .getStatusLine()
          .getStatusCode();
    }

    @Then("github respond: 404 not found")
    public void thenGithubRespond404NotFound() {
        assertTrue(SC_NOT_FOUND == githubResponseCode);
    }

    //...
}

ステップの実装で、we used the parameter injection featureがどのようになっているかに注目してください。 ステップ候補から抽出された引数は、注釈付きのJavaメソッドのパラメーターに自然な順序で一致するだけです。

また、注釈付きの名前付きパラメーターがサポートされています。

@When("I look for $username via the api")
public void whenILookForSomeNonExistentUserViaTheApi(
  @Named("username") String user) throws IOException

5.2. メディアタイプのテスト

簡単なMIMEタイプのテストストーリーは次のとおりです。

Scenario: when a user checks a valid user's profile on github, github would respond json data

Given github user profile api
And a valid username
When I look for the user via the api
Then github respond data of type json

手順は次のとおりです。

public class GithubUserResponseMediaTypeSteps {

    private String api;
    private String validUser;
    private String mediaType;

    @Given("github user profile api")
    public void givenGithubUserProfileApi() {
        api = "https://api.github.com/users/%s";
    }

    @Given("a valid username")
    public void givenAValidUsername() {
        validUser = "eugenp";
    }

    @When("I look for the user via the api")
    public void whenILookForTheUserViaTheApi() throws IOException {
        mediaType = ContentType
          .getOrDefault(getGithubUserProfile(api, validUser).getEntity())
          .getMimeType();
    }

    @Then("github respond data of type json")
    public void thenGithubRespondDataOfTypeJson() {
        assertEquals("application/json", mediaType);
    }
}

5.3. JSONペイロードのテスト

それから最後の話:

Scenario: when a user checks a valid user's profile on github, github's response json should include a login payload with the same username

Given github user profile api
When I look for eugenp via the api
Then github's response contains a 'login' payload same as eugenp

そして、単純なストレートステップの実装:

public class GithubUserResponsePayloadSteps {

    private String api;
    private GitHubUser resource;

    @Given("github user profile api")
    public void givenGithubUserProfileApi() {
        api = "https://api.github.com/users/%s";
    }

    @When("I look for $user via the api")
    public void whenILookForEugenpViaTheApi(String user) throws IOException {
        HttpResponse httpResponse = getGithubUserProfile(api, user);
        resource = RetrieveUtil.retrieveResourceFromResponse(httpResponse, GitHubUser.class);
    }

    @Then("github's response contains a 'login' payload same as $username")
    public void thenGithubsResponseContainsAloginPayloadSameAsEugenp(String username) {
        assertThat(username, Matchers.is(resource.getLogin()));
    }
}

6. 概要

この記事では、JBehaveを簡単に紹介し、BDDスタイルのREST APIテストを実装しました。

単純なJavaテストコードと比較すると、JBehaveで実装されたコードは非常に明確で直感的に見え、テスト結果レポートははるかにエレガントに見えます。

いつものように、サンプルコードはthe Github projectにあります。