我有一个Spring Boot测试,它使用wiremock来模拟外部服务。为了避免与并行构建冲突,我不想为wiremock设置固定端口号,并希望依赖其动态端口配置。
应用程序使用application.yml中设置的属性(external.baseUrl
)(在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中的值。我所看到的所有其他解决方案都使用静态值覆盖属性(例如在某些注释中),但在测试运行之前我不知道线程模式端口的值。
澄清:
spring boot和wiremock都在随机端口上运行。 那很好,我知道如何获得两个端口的价值。然而,wiremock应该模拟外部服务,我需要告诉我的应用程序如何到达它。我使用external.baseUrl
属性执行此操作。我想在测试中设置的值当然取决于线程端口号。 我的问题只是如何在弹簧启动测试中以编程方式设置属性。
答案 0 :(得分:5)
我找不到在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
替换为测试中定义的@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
MySpringBootApplication.class, MyIntegrationTest.TestConfig.class })
,以便它具有正确的Wiremock端口号。
要获取此配置,我必须在测试注释中添加此类
K.sum
请注意,我使用非静态Wiremock API,因为我有几个这样的外部服务,每个都需要被模拟。请注意,根据每个bean的设计方式,如何构建不同的bean。
答案 1 :(得分:3)
考虑使用Spring Cloud Contract Wiremock
已有JUnit规则构建器允许指定${wiremock.port}
在property / yaml文件中设置随机端口
或者您可以使用WireMockRestServiceServer
将电线摇接绑定到RestTemplate
,这样您甚至无需在测试中覆盖网址
答案 2 :(得分:2)
在application.properties中使用属性替换:
external.baseUrl=http://exampleUrl:${wiremock.server.port}
这要求在初始化SpringBootTest之前设置wiremock.server.port
属性,这可以通过在测试类中添加@AutoConfigureWireMock
批注来实现。
答案 3 :(得分:0)
这个怎么样:
@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);
}
}
答案 4 :(得分:0)
我在启动Spring Boot应用程序时用于以编程方式更改属性的方法是将自定义值传递到应用程序主入口点String[]
args。这将具有覆盖所有其他方式的效果,例如系统属性,YML或其他配置文件。
这是example:
String[] args = new String[]{"--my.prop=foo"};
SpringApplication.run(Application.class, args);
您可以轻松地公开静态方法或自定义API,以启动Spring Boot应用程序(用于测试)并使用您想要的值。
然后,一旦你拥有了线索端口的价值 - 事情就很容易了。以下是一个示例:PaymentServiceContractTest.java
P.S。空手道(我上面使用的开源测试示例)是new alternative to WireMock,请查看它;)
答案 5 :(得分:0)
至少从Spring Cloud Contract版本main
起,https://stackoverflow.com/a/48859553/309683中提到的属性名称(即wiremock.port
)不正确。
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[]
要在基于Gradle的项目中使用Spring Cloud Contract WireMock,请向您的项目添加以下依赖项:
wiremock.server.files[]
testImplementation 'org.springframework.cloud:spring-cloud-contract-wiremock:${version}'
个文件中使用如果您这样配置测试application.yaml
文件:
application.yaml
并定义以下bean:
sample:
port: ${wiremock.server.port}
您可以验证@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
答案 6 :(得分:-1)
你是如何阅读external.baseUrl
的?
如果您使用的是@Value
带注释的属性,则可以在设置模拟服务器后使用ReflectionTestUtils
设置端口。
ReflectionTestUtils.setField(yourTestClass, "youPort", wireMockServer.port());