Установить свойство с произвольным портом wiremock в тесте весенней загрузки

У меня есть тест Spring Boot, который использует Wiremock для имитации внешней службы. Чтобы избежать конфликтов с параллельными сборками, я не хочу устанавливать фиксированный номер порта для wiremock и хотел бы полагаться на его динамическую конфигурацию порта.

Приложение использует свойство (external.baseUrl), установленное в application.yml (в 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. Все другие решения, которые я рассмотрел, переопределяют свойство статическим значением (например, в некоторой аннотации), но я не знаю значение порта Wiremock до тех пор, пока не будет запущен тест.

Уточнение:

И Spring Boot, и Wiremock запускаются на случайных портах. Это нормально, и я знаю, как получить значение обоих портов. Однако предполагается, что wiremock имитирует внешнюю службу, и мне нужно указать своему приложению, как до нее добраться. Я делаю это с помощью свойства external.baseUrl. Значение, которое я хочу установить в своем тесте, конечно же, зависит от номера порта коммутации. Моя проблема заключается просто в том, как программно установить свойство в тесте весенней загрузки.


person Nicola Ambrosetti    schedule 09.02.2018    source источник


Ответы (6)


Рассмотрите возможность использования Spring Cloud Contract Wiremock

Уже существует построитель правил JUnit, который позволяет указать ${wiremock.port} для установки случайного порта в файлах свойств / yaml.

Или вы можете использовать WireMockRestServiceServer для привязки WireMock к вашему RestTemplate, чтобы вам даже не нужно было переопределять URL-адреса в ваших тестах.

person Dagon    schedule 19.02.2018
comment
Это именно то, что я искал! Я знал, что для такой распространенной проблемы должно быть стандартное решение! - person Nicola Ambrosetti; 18.01.2019

Мне не удалось найти способ переопределить свойства в тесте интеграции Spring Boot, поскольку тест запускается только после того, как приложение создано и все компоненты уже настроены.

Чтобы обойти эту проблему, я добавил @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 })

Обратите внимание, что я использую нестатический API Wiremock, поскольку у меня есть несколько таких внешних служб, каждую из которых нужно издеваться. Обратите внимание, что способ построения различных bean-компонентов зависит от того, как они были спроектированы.

person Nicola Ambrosetti    schedule 19.02.2018

Имя свойства, указанное в 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

Чтобы использовать Spring Cloud Contract WireMock в проекте на основе Gradle, добавьте в свой проект следующую зависимость:

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

4. Использование в application.yaml файлах

Если вы настроите свой тестовый application.yaml файл следующим образом:

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

И определите следующие beans:

@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 установлен на случайно выбранный порт проводного подключения:

@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")));
    }
}
person βξhrαng    schedule 23.06.2019

Используйте подстановку свойств в своем application.properties:

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

Это требует, чтобы свойство wiremock.server.port было установлено до инициализации SpringBootTest, что может быть достигнуто путем добавления аннотации @AutoConfigureWireMock в ваш тестовый класс.

person user3302637    schedule 16.01.2019
comment
Это не сработает, если wiremock.server.port определен только во время выполнения. - person Nicola Ambrosetti; 17.01.2019
comment
Я изменю свой ответ, включив в него предварительное условие - person user3302637; 18.01.2019

Подход, который я использую для программного изменения свойства при запуске приложения Spring Boot, заключается в передаче настраиваемого значения в главную точку входа приложения String[] args. Это приведет к отмене всех других средств, таких как системные свойства, YML или другие файлы конфигурации.

Вот пример:

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

Вам будет легко предоставить статический метод или пользовательский API, который запускает приложение Spring Boot (для тестирования) и с желаемым значением.

А затем, как только у вас появится ценность порта для беспроводного подключения, все станет легко. Вот пример: PaymentServiceContractTest.java

P.S. Каратэ (примеры тестов с открытым исходным кодом, которые я использую выше) - это новая альтернатива WireMock, обязательно попробуйте;)

person Peter Thomas    schedule 13.02.2018

Как ты читаешь external.baseUrl? Если вы используете аннотированное свойство @Value, вы можете использовать ReflectionTestUtils для установки порта после настройки фиктивного сервера.

ReflectionTestUtils.setField(yourTestClass, "youPort",  wireMockServer.port());
person redhunter    schedule 13.02.2018