[英]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.