【问题标题】:Set property with wiremock random port in spring boot test在春季启动测试中使用wiremock随机端口设置属性
【发布时间】:2018-07-20 07:54:55
【问题描述】:

我有一个使用wiremock 模拟外部服务的Spring Boot 测试。为了避免与并行构建发生冲突,我不想为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 中的值。我查看过的所有其他解决方案都使用静态值(例如在某些注释中)覆盖该属性,但在运行测试之前我不知道wiremock端口的值。

澄清:

spring boot 和wiremock 都在随机端口上运行。 没关系,我知道如何获取两个端口的值。但是,wiremock 应该模拟外部服务,我需要告诉我的应用程序如何访问它。我使用 external.baseUrl 属性执行此操作。我想在测试中设置的值当然取决于wiremock 端口号。 我的问题只是如何在 Spring Boot 测试中以编程方式设置属性

【问题讨论】:

    标签: java spring-boot configuration wiremock spring-boot-test


    【解决方案1】:

    https://stackoverflow.com/a/48859553/309683 中提到的属性名称(即wiremock.port)不正确,至少从 Spring Cloud Contract 版本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 还使用其他一些属性填充环境:

    1. wiremock.server.https-port
    2. wiremock.server.stubs[]
    3. 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")));
        }
    }
    

    【讨论】:

    • 当我们使用@AutoConfigureWiremock 注解时,有什么方法可以创建多个wiremock 实例?我无法在 JUnit 5 Spring-boot 测试中成功地与 AutoConfigureWiremock 注释并行运行测试。
    【解决方案2】:

    考虑使用Spring Cloud Contract Wiremock

    已经有一个 JUnit 规则构建器允许指定 ${wiremock.port} 在属性/yaml 文件中设置随机端口

    或者您可以使用 WireMockRestServiceServer 将 WireMock 绑定到您的 RestTemplate,这样您甚至不需要在测试中覆盖 URL。

    【讨论】:

    • 这正是我想要的!我就知道这样一个常见的问题应该有一个标准的解决方案!
    【解决方案3】:

    在您的 application.properties 中使用属性替换:

    external.baseUrl=http://exampleUrl:${wiremock.server.port}

    这需要在 SpringBootTest 初始化之前设置 wiremock.server.port 属性,这可以通过在测试类中添加 @AutoConfigureWireMock 注释来实现。

    【讨论】:

    • 如果wiremock.server.port 仅在运行时定义,这将不起作用。
    • 将更改我的答案以包含先决条件
    【解决方案4】:

    我找不到在 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 替换为测试中定义的那个,因此它具有正确的Wiremock 端口号。

    为了获得这个配置,我必须在测试注释中添加这个类

    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
        MySpringBootApplication.class, MyIntegrationTest.TestConfig.class })
    

    请注意,我使用的是非静态 Wiremock API,因为我有几个这样的外部服务,每个服务都需要模拟。请注意,不同 bean 的构建方式因设计方式而异。

    【讨论】:

      【解决方案5】:

      external.baseUrl 的阅读情况如何? 如果您使用的是@Value 注释属性,则可以在设置模拟服务器后使用ReflectionTestUtils 设置端口。

      ReflectionTestUtils.setField(yourTestClass, "youPort",  wireMockServer.port());
      

      【讨论】:

        【解决方案6】:

        我在启动 Spring Boot 应用程序时以编程方式更改属性的方法是将自定义值传递到应用程序主入口点 String[] args。这将具有覆盖所有其他方式的效果,例如系统属性、YML 或其他配置文件。

        这是example

        String[] args = new String[]{"--my.prop=foo"};
        SpringApplication.run(Application.class, args);
        

        您可以轻松地公开启动 Spring Boot 应用程序(用于测试)并具有您想要的值的静态方法或自定义 API。

        然后,一旦你拥有了wiremock 端口的价值——事情就变得简单了。这是一个例子:PaymentServiceContractTest.java

        附:空手道(我在上面使用的开源测试示例)是new alternative to WireMock,请检查一下;)

        【讨论】:

          猜你喜欢
          • 2018-01-21
          • 1970-01-01
          • 1970-01-01
          • 2017-12-09
          • 2016-12-13
          • 1970-01-01
          • 1970-01-01
          • 2017-01-27
          • 2022-08-20
          相关资源
          最近更新 更多