在 spring 启动测试中使用 wiremock 随机端口设置 属性
Set property with wiremock random port in spring boot test
我有一个 Spring 启动测试,它使用 wiremock 模拟外部服务。为了避免与并行构建发生冲突,我不想为 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 端口的值。
澄清:
spring 在随机端口上启动和 wiremock 运行。 很好,我知道如何获取两个端口的值。然而,wiremock 应该模拟外部服务,我需要告诉我的应用程序如何访问它。我使用 external.baseUrl
属性 执行此操作。我想在测试中设置的值当然取决于 wiremock 端口号。 我的问题只是如何在 spring 引导测试中以编程方式设置 属性。
我在启动 Spring 引导应用程序时以编程方式更改 属性 的方法是将自定义值传递到应用程序主入口点 String[]
args。这将具有覆盖所有其他方式的效果,例如系统属性、YML 或其他配置文件。
这是一个example:
String[] args = new String[]{"--my.prop=foo"};
SpringApplication.run(Application.class, args);
您可以很容易地公开静态方法或自定义 API 来启动 Spring 启动应用程序(用于测试)并使用您想要的值。
然后,一旦您获得了 wiremock 端口的值 - 事情就很简单了。这是一个例子:PaymentServiceContractTest.java
P.S。空手道(我在上面使用的开源测试示例)是 new alternative to WireMock,一定要检查一下 ;)
你读得怎么样 external.baseUrl
?
如果您使用 @Value
注释 属性,您可以在设置模拟服务器后使用 ReflectionTestUtils
设置端口。
ReflectionTestUtils.setField(yourTestClass, "youPort", wireMockServer.port());
考虑使用 Spring Cloud Contract Wiremock
已经有一个 JUnit 规则构建器允许指定 ${wiremock.port}
以在 property/yaml 个文件中设置随机端口
或者您可以使用 WireMockRestServiceServer
将 WireMock 绑定到您的 RestTemplate
,这样您甚至不需要在测试中覆盖 URL。
我找不到在 Spring 引导集成测试中覆盖属性的方法,因为测试 运行 只有在创建应用程序并且所有 bean 都已配置之后。
作为解决方法,我在测试中添加了 @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
替换为测试中定义的那个,以便它具有正确的 Wiremock 端口号。
要获取此配置,我必须在测试注释中添加此 class
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
MySpringBootApplication.class, MyIntegrationTest.TestConfig.class })
请注意,我使用 non-static Wiremock API,因为我有几个这样的外部服务,每个都需要被模拟。请注意,不同 bean 的构建方式因每个 bean 的设计方式而异。
在 application.properties 中使用 属性 替换:
external.baseUrl=http://exampleUrl:${wiremock.server.port}
这需要在初始化 SpringBootTest 之前设置 wiremock.server.port
属性,这可以通过在测试 class 中添加 @AutoConfigureWireMock
注释来实现。
中提到的属性名称(即wiremock.port
)不正确,至少自Spring云合同版本2.1.2.RELEASE
以来是这样。
1。工作示例
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+");
}
}
2。其他 WireMock 属性
除 wiremock.server.port
外,@AutoConfigureWireMock
还使用其他一些属性填充环境:
wiremock.server.https-port
wiremock.server.stubs[]
wiremock.server.files[]
3。 Gradle 依赖项
要在基于 Gradle 的项目中使用 Spring Cloud Contract WireMock,请将以下依赖项添加到您的项目中:
testImplementation 'org.springframework.cloud:spring-cloud-contract-wiremock:${version}'
4。在 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
是否设置为随机选择的 wiremock 端口:
@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 启动测试,它使用 wiremock 模拟外部服务。为了避免与并行构建发生冲突,我不想为 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 端口的值。
澄清:
spring 在随机端口上启动和 wiremock 运行。 很好,我知道如何获取两个端口的值。然而,wiremock 应该模拟外部服务,我需要告诉我的应用程序如何访问它。我使用 external.baseUrl
属性 执行此操作。我想在测试中设置的值当然取决于 wiremock 端口号。 我的问题只是如何在 spring 引导测试中以编程方式设置 属性。
我在启动 Spring 引导应用程序时以编程方式更改 属性 的方法是将自定义值传递到应用程序主入口点 String[]
args。这将具有覆盖所有其他方式的效果,例如系统属性、YML 或其他配置文件。
这是一个example:
String[] args = new String[]{"--my.prop=foo"};
SpringApplication.run(Application.class, args);
您可以很容易地公开静态方法或自定义 API 来启动 Spring 启动应用程序(用于测试)并使用您想要的值。
然后,一旦您获得了 wiremock 端口的值 - 事情就很简单了。这是一个例子:PaymentServiceContractTest.java
P.S。空手道(我在上面使用的开源测试示例)是 new alternative to WireMock,一定要检查一下 ;)
你读得怎么样 external.baseUrl
?
如果您使用 @Value
注释 属性,您可以在设置模拟服务器后使用 ReflectionTestUtils
设置端口。
ReflectionTestUtils.setField(yourTestClass, "youPort", wireMockServer.port());
考虑使用 Spring Cloud Contract Wiremock
已经有一个 JUnit 规则构建器允许指定 ${wiremock.port}
以在 property/yaml 个文件中设置随机端口
或者您可以使用 WireMockRestServiceServer
将 WireMock 绑定到您的 RestTemplate
,这样您甚至不需要在测试中覆盖 URL。
我找不到在 Spring 引导集成测试中覆盖属性的方法,因为测试 运行 只有在创建应用程序并且所有 bean 都已配置之后。
作为解决方法,我在测试中添加了 @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
替换为测试中定义的那个,以便它具有正确的 Wiremock 端口号。
要获取此配置,我必须在测试注释中添加此 class
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
MySpringBootApplication.class, MyIntegrationTest.TestConfig.class })
请注意,我使用 non-static Wiremock API,因为我有几个这样的外部服务,每个都需要被模拟。请注意,不同 bean 的构建方式因每个 bean 的设计方式而异。
在 application.properties 中使用 属性 替换:
external.baseUrl=http://exampleUrl:${wiremock.server.port}
这需要在初始化 SpringBootTest 之前设置 wiremock.server.port
属性,这可以通过在测试 class 中添加 @AutoConfigureWireMock
注释来实现。
wiremock.port
)不正确,至少自Spring云合同版本2.1.2.RELEASE
以来是这样。
1。工作示例
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+");
}
}
2。其他 WireMock 属性
除 wiremock.server.port
外,@AutoConfigureWireMock
还使用其他一些属性填充环境:
wiremock.server.https-port
wiremock.server.stubs[]
wiremock.server.files[]
3。 Gradle 依赖项
要在基于 Gradle 的项目中使用 Spring Cloud Contract WireMock,请将以下依赖项添加到您的项目中:
testImplementation 'org.springframework.cloud:spring-cloud-contract-wiremock:${version}'
4。在 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
是否设置为随机选择的 wiremock 端口:
@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")));
}
}