java 在春季启动测试中使用线模随机端口设置属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48707625/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 10:08:48  来源:igfitidea点击:

Set property with wiremock random port in spring boot test

javaspring-bootconfigurationwiremockspring-boot-test

提问by Nicola Ambrosetti

I have a Spring Boot test that uses wiremock to mock an external service. In order to avoid conflicts with parallel builds I don't want to set a fixed port number for wiremock and would like to rely on its dynamic port configuration.

我有一个 Spring Boot 测试,它使用 wiremock 来模拟外部服务。为了避免与并行构建发生冲突,我不想为 wiremock 设置固定端口号,而是希望依赖其动态端口配置。

The application uses a property (external.baseUrl) set in the application.yml (under src/test/resources). However I didn't find a way to programmatically override that. I've tried something like this:

应用程序使用 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());

but it didn't work and the value in application.yml was used instead. All other solutions that I've looked at override the property with a static value (for example in some annotation), but I don't know the value of the wiremock port until the test is run.

但它不起作用,而是使用 application.yml 中的值。我查看过的所有其他解决方案都使用静态值覆盖了该属性(例如在某些注释中),但是在运行测试之前我不知道 wiremock 端口的值。

Clarification:

澄清:

Both spring boot and wiremock run on random ports. That's fine and I know how to get the value of both ports. However wiremock is supposed to mock an external service and I need to tell my application how to reach it. I do this with the external.baseUrlproperty. The value I want to set in my test depends of course on the wiremock port number. My problem is simply how to programmatically set a property in a spring boot test.

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

采纳答案by Dagon

Consider using Spring Cloud Contract Wiremock

考虑使用Spring Cloud Contract Wiremock

There is already a JUnit Rule builder which allows to specify ${wiremock.port}to set random port in property/yaml files

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

Or you can use WireMockRestServiceServerto bind WireMock to your RestTemplateso you don't even need to override URLs in your tests.

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

回答by Nicola Ambrosetti

I could not find a way to override properties in a Spring Boot integration test, since the test is run only after the application is created and all the beans already configured.

我找不到在 Spring Boot 集成测试中覆盖属性的方法,因为该测试仅在创建应用程序并且所有 bean 都已配置后运行。

As a work around I added a @TestConfigurationto the test to replace the beans in the application:

作为一种解决方法,我@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");
    }
}

This effectively replaced the BeanUsingAPropertywith the one defined in the test so that it has the correct port number for Wiremock.

这有效地用BeanUsingAProperty测试中定义的替换了,以便它具有正确的 Wiremock 端口号。

For this configuration to be picked up I had to add this class in the test annotation

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

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

Note that I use the non-static Wiremock API, since I have several such external services that each need to be mocked. Note that how the different beans are built is different depending on how each was designed.

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

回答by user3302637

Use property substitution in your application.properties:

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

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

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

This requires the wiremock.server.portproperty to be set before the SpringBootTest is initialised, which can be achieved by adding the @AutoConfigureWireMockannotation to your test class.

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

回答by Behrang Saeedzadeh

The property name mentioned in https://stackoverflow.com/a/48859553/309683(i.e. wiremock.port) is not correct, at least since Spring Cloud Contract version 2.1.2.RELEASE.

https://stackoverflow.com/a/48859553/309683(ie wiremock.port) 中提到的属性名称不正确,至少从 Spring Cloud Contract version 开始2.1.2.RELEASE

1. Working example

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. Other WireMock properties

2. 其他 WireMock 属性

Other than wiremock.server.port, @AutoConfigureWireMockpopulates the environment with some other properties too:

除了wiremock.server.port, 还@AutoConfigureWireMock用其他一些属性填充环境:

  1. wiremock.server.https-port
  2. wiremock.server.stubs[]
  3. wiremock.server.files[]
  1. wiremock.server.https-port
  2. wiremock.server.stubs[]
  3. wiremock.server.files[]

3. Gradle dependencies

3. Gradle 依赖

To use Spring Cloud Contract WireMock in a Gradle based project, add the following dependency to your project:

要在基于 Gradle 的项目中使用 Spring Cloud Contract WireMock,请将以下依赖项添加到您的项目中:

testImplementation 'org.springframework.cloud:spring-cloud-contract-wiremock:${version}'

4. Using in application.yamlfiles

4. 在application.yaml文件中使用

If you configure your test application.yamlfile like this:

如果您application.yaml像这样配置测试文件:

sample:
  port: ${wiremock.server.port}

And define the following beans:

并定义以下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();
    }
}

You can verify that sample.portis set to the randomly chosen wiremock port:

您可以验证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")));
    }
}

回答by luboskrnac

What about this:

那这个呢:

@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);
    }
}

回答by Peter Thomas

The approach I use to programmatically change a property when starting a Spring Boot app, is to pass the custom value into the application main entry-point String[]args. This will have the effect of over-riding all other means such as System properties, YML or other config files.

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

Here is an example:

这是一个例子

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

It will be easy for you to expose a static method or custom API which starts the Spring Boot app (for testing) and with the value you want.

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

And then, once you have the value of the wiremock port - things are easy. Here is an example: PaymentServiceContractTest.java

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

P.S. Karate (the open-source test examples I am using above) is a new alternative to WireMock, do check it out ;)

PS Karate(我在上面使用的开源测试示例)是WireMock新替代品,请检查一下;)

回答by redhunter

How are you reading external.baseUrl? If you are using a @Valueannotated property, you can use ReflectionTestUtilsto set the port after you have setup the mock server.

你读external.baseUrl得怎么样?如果您使用的是带@Value注释的属性,则可以ReflectionTestUtils在设置模拟服务器后使用它来设置端口。

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