kagamihogeの日記

kagamihogeの日記です。

spring-boot + MockServer

spring-bootのweb-apiのintegration testでMockServerを使用する。

ソースコード

build.gradle

springとの連携用のmockserver-spring-test-listener-no-dependenciesの依存性を追加する。

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

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

configurations {
  compileOnly {
    extendsFrom annotationProcessor
  }
}

repositories {
  mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-web'
  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.mock-server:mockserver-spring-test-listener-no-dependencies:5.13.2'
  
}

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

外部アクセスを伴う適当なrest-apiを作成する。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@SpringBootApplication()
public class SampleWireMockApplication {
  @Value("${sample.remote.path}")
  String path;

  @RequestMapping("/sample/{key}")
  public String sample(@PathVariable String key) {
    RestTemplate template = new RestTemplate();

    String result = template.getForObject(path + "/sample-api/" + key, String.class);
    return result;
  }

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

プロパティファイルに外部アクセス先URLを持つ。

server.port=8081

sample.remote.path=http://example.com:12345

テストコードを作成する。

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.Map;

import org.junit.jupiter.api.Test;
import org.mockserver.client.MockServerClient;
import org.mockserver.model.Header;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import org.mockserver.springtest.MockServerTest;
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.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;

@SpringBootTest(properties = { "sample.remote.path=${server.url}" }, webEnvironment = WebEnvironment.RANDOM_PORT)
@MockServerTest("server.url=http://localhost:${mockServerPort}")
public class ApplicationTest {
  @LocalServerPort
  int serverPort;

  @Autowired
  TestRestTemplate template;

  MockServerClient mockServerClient;

  @Test
  void test() {
    mockServerClient
      .when(HttpRequest
          .request()
          .withMethod("GET")
          .withPath("/sample-api/key123"))
      .respond(HttpResponse
          .response()
          .withStatusCode(200)
          .withHeader(
              new Header("Content-Type", "application/json; charset=utf-8"))
          .withBody("""
                  {
                    "sample-id": "id12345",
                    "sample-value": "value"
                  }
                  """));

    Map<String, String> actual = template.getForObject("http://localhost:" + serverPort + "/sample/key123", Map.class);
    Map<String, String> expected = Map.of("sample-id", "id12345", "sample-value", "value");

    assertEquals(expected, actual);
  }
}
  • webEnvironment = WebEnvironment.RANDOM_PORTでランダムポートでこのapiが起動する。
  • @MockServerTestでspring連携が有効になりMockServerClientフィールドを宣言すれば使用可能になる。
  • @MockServerTestserver.url=http://localhost:${mockServerPort}でmock-serverのURLを指定できる。
  • @SpringBootTestpropertiesで外部アクセスURLのプロパティをmock-serverのURLで上書きする。
  • mockServerClient.when...でモックの定義をする。
  • TestRestTemplateRestTemplateでも構わない。これからはWebTestClientが主流になるかも?
  • assertEqualsはサンプルコードなので簡易にMapでの検証にした。実際にはrecordとかデータ保持クラスに詰め替えたりしてから検証、になると思われる。

関連