1概要

この簡単な記事では、消費者主導型契約の概念に注目します。



Pact


ライブラリを使用して定義した契約を通じて、外部のRESTサービスとの統合をテストします。

その契約はクライアントによって定義され、それからプロバイダによって拾われて、そのサービスの開発のために使われることができます。

また、クライアントアプリケーションとプロバイダーアプリケーションの両方の契約に基づいてテストを作成します。


2

Pact

とは何ですか?


  • Pactを使用して

    、契約の形で特定のプロバイダ(HTTP RESTサービスにすることができます)に対する消費者の期待を定義できます(したがってライブラリの名前)。

この契約は、

Pact

が提供するDSLを使用して設定します。

定義したら、定義した契約に基づいて作成されたモックサービスを使用して、消費者とプロバイダの間のやり取りをテストできます。また、モッククライアントを使用して、契約に対してサービスをテストします。


3 Mavenの依存関係

はじめに、https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22au.com.dius%22%20AND%20a%3A%22pactにMavenの依存関係を追加する必要があります。 -jvm-consumer-junit

2.11%22[

pact-jvm-consumer-junit

2.11

]ライブラリ:

<dependency>
    <groupId>au.com.dius</groupId>
    <artifactId>pact-jvm-consumer-junit__2.11</artifactId>
    <version>3.5.0</version>
    <scope>test</scope>
</dependency>


4契約を定義する


Pact

を使用してテストを作成したい場合は、まずテストで使用する

@ Rule

を定義する必要があります。

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

プロバイダ名、ホスト、およびサーバーモック(契約から作成される)が起動されるポートを渡します。

サービスが処理できる2つのHTTPメソッドの規約を定義したとしましょう。

最初の方法は、2つのフィールドを持つJSONを返すGETリクエストです。

リクエストが成功すると、200 HTTPレスポンスコードとJSONのC

__ontent-Type

__ヘッダを返します。

そのような契約を

Pact

を使って定義しましょう。


  • @ Pact

    アノテーションを使用し、コントラクトが定義されているコンシューマ名を渡す必要があります。** アノテーション付きメソッド内で、GETコントラクトを定義できます。

@Pact(consumer = "test__consumer")
public RequestResponsePact createPact(PactDslWithProvider builder) {
    Map<String, String> 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\"}")
        (...)
}


Pact

DSLを使って、与えられたGETリクエストに対して、特定のヘッダとボディを含む200レスポンスを返すように定義します。

私たちの契約の2番目の部分はPOSTメソッドです。クライアントが適切なJSON本文を含むパス

/pact

にPOST要求を送信すると、201 HTTP応答コードが返されます。

そのような契約を

Pactで定義しましょう:

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


RequestResponsePact

のインスタンスを返すには、契約の終わりに

toPact()

メソッドを呼び出す必要があることに注意してください。


4.1. 結果の協定アーティファクト

デフォルトでは、Pactファイルは

target/pacts

フォルダーに生成されます。

このパスをカスタマイズするには、__maven-surefire-pluginを設定します。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <systemPropertyVariables>
            <pact.rootDir>target/mypacts</pact.rootDir>
        </systemPropertyVariables>
    </configuration>
    ...
</plugin>

Mavenビルドは

target/mypacts

フォルダーに

test

consumer-test

provider.json

という名前のファイルを生成します。このファイルには要求と応答の構造が含まれています。

{
    "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契約を使用したクライアントとプロバイダのテスト

これで契約ができたので、クライアントとプロバイダの両方に対してそれに対するテストを作成するために使用できます。

これらのテストのそれぞれは、契約に基づいている、対応するもののモックを使用します。

  • クライアントはモックプロバイダを使用します

  • プロバイダはモッククライアントを使用します

実質的に、テストは契約に対して行われます。


5.1. クライアントをテストする

契約を定義したら、その契約に基づいて作成されるサービスとのやり取りをテストできます。

通常のJUnitテストを作成できますが、テストの最初に

@ PactVerification

アノテーションを付けることを忘れないでください。

GETリクエストのテストを書きましょう。

@Test
@PactVerification()
public void givenGet__whenSendRequest__shouldReturn200WithProperHeaderAndBody() {

   //when
    ResponseEntity<String> 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");
}


  • @ PactVerification

    アノテーションはHTTPサービスを開始する面倒を見ます** テストでは、GETリクエストを送信し、レスポンスがコントラクトに準拠していることをアサートするだけです。

POSTメソッド呼び出しのテストも追加しましょう。

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

ご覧のとおり、POSTリクエストのレスポンスコードは201です。これは

Pact

コントラクトで定義されたとおりです。


@ PactVerification()

アノテーションを使用していたので、

Pact

ライブラリはテストケースの前に以前に定義されたコントラクトに基づいてWebサーバーを起動しています。


5.2. プロバイダのテスト

私たちの契約検証の2番目のステップは、契約に基づいてモッククライアントを使ってプロバイダのテストを作成することです。

私たちのプロバイダーの実装はTDD方式でこの契約によって推進されます。

この例では、Spring Boot REST APIを使用します。

まず、JUnitテストを作成するために、https://search.maven.org/classic/#search%7Cga%7C1%7Ca%3A%22pact-jvm-provider-junit

2.11%22[]を追加する必要があります。 pact-jvm-provider-junit

2.11]依存関係:

<dependency>
    <groupId>au.com.dius</groupId>
    <artifactId>pact-jvm-provider-junit__2.11</artifactId>
    <version>3.5.0</version>
    <scope>test</scope>
</dependency>

これにより、

PactRunner

を使用してプロバイダ名とPactアーティファクトの場所を指定してJUnitテストを作成できます。

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

この設定が機能するには、

test

consumer-test

provider.json

ファイルをRESTサービスプロジェクトの

pacts

フォルダーに配置する必要があります。

次に、契約内のやり取りを検証するために使用するターゲットを定義し、テストを実行する前にSpring Bootアプリを起動します。

@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);
}

最後に、テストしたい契約内の州を指定します。

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

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

このJUnitクラスを実行すると、2つのGETリクエストとPOSTリクエストに対して2つのテストが実行されます。ログを見てみましょう。

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)

ここではRESTサービスを作成するためのコードを含めていません。

完全なサービスとテストはhttps://github.com/eugenp/tutorials/tree/master/spring-rest[GitHubプロジェクト]にあります。


6. 結論

この簡単なチュートリアルでは、Consumer Driven Contractを見ました。


Pact

ライブラリを使用して契約を作成しました。契約を定義したら、契約に対してクライアントとサービスをテストし、それらが仕様に準拠していることを表明することができました。

これらすべての例とコードスニペットの実装はhttps://github.com/eugenp/tutorials/tree/master/libraries[GitHubプロジェクト]にあります – これはMavenプロジェクトなので、インポートするのは簡単ですし、そのまま実行します。