Wiremockを使用して外部サービスをモックするSpring Bootテストがあります。並列ビルドとの競合を回避するために、wiremockに固定ポート番号を設定したくないので、動的ポート構成に依存したいと思います。
アプリケーションはプロパティ(external.baseUrl
)application.ymlで設定します(src/test/resourcesの下)。しかし、それをプログラムでオーバーライドする方法は見つかりませんでした。私はこのようなものを試しました:
WireMockServer wireMockServer = new WireMockServer();
wireMockServer.start();
WireMock mockClient = new WireMock("localhost", wireMockServer.port());
System.setProperty("external.baseUrl", "http://localhost:" + wireMockServer.port());
しかし、それは機能せず、代わりにapplication.ymlの値が使用されました。私が調べた他のすべてのソリューションは、静的な値(たとえば、いくつかの注釈)でプロパティをオーバーライドしますが、テストが実行されるまで、ワイヤモックポートの値はわかりません。
説明:
スプリングブートとワイヤモックの両方がランダムなポートで実行されます。 それは問題なく、両方のポートの値を取得する方法を知っています。ただし、wiremockは外部サービスをモックすることになっているため、アプリケーションに到達する方法をアプリケーションに通知する必要があります。私はexternal.baseUrl
プロパティ。テストで設定する値は、もちろん、Wirmockポート番号によって異なります。 私の問題は、スプリングブートテストでプログラムによってプロパティを設定する方法です。
Spring Cloud Contract Wiremock の使用を検討してください
プロパティ/ yamlファイルにランダムなポートを設定するために${wiremock.port}
を指定できるJUnitルールビルダーがすでにあります
または、WireMockRestServiceServer
を使用してWireMockをRestTemplate
にバインドできるため、テストでURLをオーバーライドする必要もありません。
アプリケーションが作成され、すべてのBeanがすでに構成されている場合にのみテストが実行されるため、Spring Boot統合テストでプロパティをオーバーライドする方法が見つかりませんでした。
回避策として、@TestConfiguration
テストを実行して、アプリケーションのBeanを置き換えます。
private static WireMockServer wireMockServer1 = getWireMockServer();
private static WireMockServer wireMockServer2 = getWireMockServer();
private static WireMockServer wireMockServer3 = getWireMockServer();
private static WireMockServer getWireMockServer() {
final WireMockServer wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
return wireMockServer;
}
@TestConfiguration
static class TestConfig {
@Bean
@Primary
public BeanUsingAProperty1 getBean1() {
BeanUsingAProperty myBean = new BeanUsingAProperty();
myBean.setPort(wireMockServer.port());
return myBean;
}
@Bean
@Primary
public BeanUsingAProperty2 getBean2() {
String baseUrl = "http://localhost:" + wireMockServer2.port();
return new BeanUsingAProperty2(baseUrl);
}
@Bean
@Primary
public BeanUsingAProperty3 getBean3() {
String baseUrl = "http://localhost:" + wireMockServer3.port() + "/request";
return new BeanUsingAProperty3(new RestTemplate(), baseUrl, "someOtherParameter");
}
}
これにより、BeanUsingAProperty
がテストで定義されたものと効果的に置き換えられ、Wirmockの正しいポート番号が設定されました。
この構成をピックアップするには、このクラスをテストアノテーションに追加する必要がありました。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
MySpringBootApplication.class, MyIntegrationTest.TestConfig.class })
静的なWiremock APIを使用していることに注意してください。モックする必要がある外部サービスがいくつかあるからです。異なるBeanの作成方法は、それぞれの設計方法によって異なることに注意してください。
Application.propertiesでプロパティ置換を使用します。
external.baseUrl=http://exampleUrl:${wiremock.server.port}
これには、SpringBootTestが初期化される前にwiremock.server.port
プロパティを設定する必要があります。これは、テストクラスに@AutoConfigureWireMock
アノテーションを追加することで実現できます。
https://stackoverflow.com/a/48859553/30968 (つまり、wiremock.port
)に記載されているプロパティ名は、少なくともSpring Cloud契約バージョン2.1.2.RELEASE
以降、正しくありません。
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@AutoConfigureWireMock(port = 0)
public class PortServiceTest {
@Autowired
private Environment environment;
@Test
public void shouldPopulateEnvironmentWithWiremockPort() {
assertThat(environment.containsProperty("wiremock.server.port")).isTrue();
assertThat(environment.getProperty("wiremock.server.port")).matches("\\d+");
}
}
wiremock.server.port
以外の@AutoConfigureWireMock
は、他のいくつかのプロパティも環境に入力します。
wiremock.server.https-port
wiremock.server.stubs[]
wiremock.server.files[]
GradleベースのプロジェクトでSpring Cloud Contract WireMockを使用するには、プロジェクトに次の依存関係を追加します。
testImplementation 'org.springframework.cloud:spring-cloud-contract-wiremock:${version}'
application.yaml
ファイルでの使用次のようにテストapplication.yaml
ファイルを構成する場合:
sample:
port: ${wiremock.server.port}
次のBeanを定義します。
@Component
@ConfigurationProperties(prefix = "sample")
@Data
public class PortProperties {
private Integer port;
}
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class PortService {
private final PortProperties config;
public Integer getPort() {
return config.getPort();
}
}
sample.port
がランダムに選択されたワイヤモックポートに設定されていることを確認できます。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@AutoConfigureWireMock(port = 0)
public class PortServiceTest {
@Autowired
private Environment environment;
@Autowired
private PortService portService;
@Test
public void shouldReturnWireMockPort() {
assertThat(portService.getPort())
.isNotNull()
.isEqualTo(Integer.parseInt(environment.getProperty("wiremock.server.port")));
}
}
Spring Bootアプリの起動時にプログラムでプロパティを変更するために使用するアプローチは、カスタム値をアプリケーションのメインエントリポイントString[]
argsに渡すことです。これは、システムプロパティ、YML、その他の構成ファイルなど、他のすべての手段を上書きする効果があります。
これが 例 です:
String[] args = new String[]{"--my.prop=foo"};
SpringApplication.run(Application.class, args);
Spring Bootアプリ(テスト用)を起動し、必要な値で静的メソッドまたはカスタムAPIを公開するのは簡単です。
そして、Wirmockポートの価値があれば、簡単です。次に例を示します。 PaymentServiceContractTest.Java
追伸空手(上記で使用しているオープンソースのテスト例)は WireMockの新しい代替手段 です。確認してください;)
これはどうですか:
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
class YourTestClass {
@LocalServerPort
int port;
public void test() {
WireMockServer wireMockServer = new WireMockServer(port);
wireMockServer.start();
WireMock mockClient = new WireMock("localhost", port);
}
}