![](/img/trans.png)
[英]Spring boot integration test error: “Could not resolve placeholder 'wiremock.server.port'” in tests not requiring Wiremock
[英]Set property with wiremock random port in spring boot test
我有一个 Spring 启动测试,它使用 wiremock 来模拟外部服务。 为了避免与并行构建发生冲突,我不想为 wiremock 设置固定端口号,而是希望依赖其动态端口配置。
该应用程序使用在 application.yml(在 src/test/resources 下)中设置的属性 ( external.baseUrl
)。 但是我没有找到以编程方式覆盖它的方法。 我试过这样的事情:
WireMockServer wireMockServer = new WireMockServer();
wireMockServer.start();
WireMock mockClient = new WireMock("localhost", wireMockServer.port());
System.setProperty("external.baseUrl", "http://localhost:" + wireMockServer.port());
但它不起作用,而是使用了 application.yml 中的值。 我看过的所有其他解决方案都使用 static 值覆盖该属性(例如在某些注释中),但在测试运行之前我不知道 wiremock 端口的值。
澄清:
spring boot 和 wiremock 都在随机端口上运行。 很好,我知道如何获得两个端口的值。 然而,wiremock 应该模拟外部服务,我需要告诉我的应用程序如何访问它。 我使用external.baseUrl
属性执行此操作。 我想在测试中设置的值当然取决于 wiremock 端口号。 我的问题只是如何以编程方式在 spring 启动测试中设置属性。
我找不到在Spring Boot集成测试中覆盖属性的方法,因为测试仅在创建应用程序并且已经配置了所有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
替换为测试中定义的BeanUsingAProperty
,以便它具有Wiremock的正确端口号。
要获取此配置,我必须在测试注释中添加此类
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
MySpringBootApplication.class, MyIntegrationTest.TestConfig.class })
请注意,我使用非静态Wiremock API,因为我有几个这样的外部服务,每个都需要被模拟。 请注意,根据每个bean的设计方式,如何构建不同的bean。
考虑使用Spring Cloud Contract Wiremock
已有一个JUnit Rule构建器允许指定${wiremock.port}
来设置property / yaml文件中的随机端口
或者您可以使用WireMockRestServiceServer
将WireMock绑定到RestTemplate
这样您甚至不需要覆盖测试中的URL。
在application.properties中使用属性替换:
external.baseUrl=http://exampleUrl:${wiremock.server.port}
这需要在SpringBootTest初始化之前设置wiremock.server.port
属性,这可以通过将@AutoConfigureWireMock
注释添加到测试类来实现。
https://stackoverflow.com/a/48859553/309683 (即wiremock.port
)中提到的属性名称不正确,至少从Spring Cloud Contract 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
是否设置为随机选择的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")));
}
}
那这个呢:
@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);
}
}
我在启动Spring Boot应用程序时用于以编程方式更改属性的方法是将自定义值传递到应用程序主入口点String[]
args。 这将具有覆盖所有其他方式的效果,例如系统属性,YML或其他配置文件。
这是一个例子 :
String[] args = new String[]{"--my.prop=foo"};
SpringApplication.run(Application.class, args);
您可以轻松地公开静态方法或自定义API,以启动Spring Boot应用程序(用于测试)并使用您想要的值。
然后,一旦你拥有了线索端口的价值 - 事情很简单。 这是一个例子: PaymentServiceContractTest.java
当你使用 org.springframework.cloud:spring-cloud-contract-wiremock 依赖时,如果你喜欢注释,你可以添加@AutoConfigureWireMock(port = Options.DYNAMIC_PORT)
你是如何阅读external.baseUrl
? 如果您使用的是@Value
annotated属性,则可以在设置模拟服务器后使用ReflectionTestUtils
设置端口。
ReflectionTestUtils.setField(yourTestClass, "youPort", wireMockServer.port());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.