kagamihogeの日記

kagamihogeの日記です。

WireMockのResponse Templatingでレスポンスを動的に書換

WireMockresponse-templatingによりレスポンスを書き換えられる。以下にspring-bootの@SpringBootTestを前提にしたサンプルコード・使い方について書く。

ソースコード

build.gradle

plugins {
    id 'org.springframework.boot' version '2.7.2'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "2021.0.3")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-stub-runner'
    
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

tasks.named('test') {
    useJUnitPlatform()
}

application.properties

外部APIを想定したテストなのでsrc/main/resources/application.propertiesにURLを定義しておく。自動テスト実行時にはWireMockのモックサーバーのURLになる。

external.api.url=http://localhost:12345

java

パスをそのままWireMockに投げてレスポンスもそのまま返すだけのマッピングを作っておく。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Flux;

@RestController
@SpringBootApplication()
public class SampleWireMockApplication {
  WebClient client;
  
  SampleWireMockApplication(WebClient.Builder webClientBuilder, Environment env) {
    this.client = webClientBuilder.baseUrl(env.getProperty("external.api.url")).build();
  }

  @GetMapping("/**")
  public Flux<String> sample(ServerWebExchange exchange) {
    return client
        .get().uri(exchange.getRequest().getPath().value())
        .retrieve()
        .bodyToFlux(String.class);
  }

  public static void main(String[] args) {
    SpringApplication.run(SampleWireMockApplication.class, args);
  }
}

SpringBootTestのテストコード

上記のcontrollerにアクセスしてレスポンスを返すだけのテストコードを書く。本来はここで各種expectをするのだけど今回そこは主題ではないので省略する。

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.test.web.reactive.server.WebTestClient;

@SpringBootTest(properties = {
    "external.api.url=http://localhost:${wiremock.server.port}" },
    webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureWireMock(port = 0)
public class ApplicationTest {

  @Autowired
  WebTestClient client;

  @Test
  void testSample() {
    String result = client
        .get().uri("/hoge")
        .exchange()
        .returnResult(String.class)
        .getResponseBody()
        .blockFirst();
    System.out.println(result);
  }
}

WireMock

mapping

/src/test/resources/mappings/sample-mapping.json マッピングファイルを作る。

{
  "request": {
    "method": "GET",
    "urlPathPattern": "/(.*)"
  },
  "response": {
    "status": "200",
    "body": "{{request.path}}",
    "transformers": ["response-template"]
  }
}

実行結果

これを実行するとレスポンスに/hogeと返ってくる。

response-template

基本的な使い方

まずtransformersresponse-templateの追加が必要。これによりresponse-templatingのThe request modelの各種組み込み変数やhelper関数が使用可能となる。以降、マッピングファイルのサンプルコードはresponse部分のみに省略。

  "response": {
    "status": "200",
    "body": "{{request.path}}",
    "transformers": ["response-template"]
  }

ボディファイルを動的に変更

例えば、以下のようにパスで返すボディファイルを変更したい、とする。

  • /hoge.json -> __files/sample/hoge.json
  • /foo.json -> __files/sample/foo.json
  "response": {
    "status": "200",
    "bodyFileName": "sample/{{request.path.[0]}}",
    "transformers": ["response-template"]
  }

ボディファイルの中身を動的に変更

ボディファイルの中身にも{{request.path.[0]}}などを書ける。

まず以下のようにボディファイルを返すマッピングを作る。

  "response": {
    "status": "200",
    "bodyFileName": "sample/foo.json",
    "transformers": ["response-template"]
  }

次にボディファイルの中身をリクエストパスで動的に書き換える。

{
  "id": "{{request.path}}"
}

これで例えば/hoge-pathにアクセスするとレスポンスボディは{ "id": "/hoge-path"}が返ってくる。

ドキュメント