wake-up-neo.com

Legen Sie im Spring Boot-Test die Eigenschaft mit dem drahtlosen Zufallsport fest

Ich habe einen Spring Boot-Test, der Wiremock verwendet, um einen externen Dienst zu verspotten. Um Konflikte mit parallelen Builds zu vermeiden, möchte ich keine feste Portnummer für Wiremock festlegen und möchte mich auf seine dynamische Portkonfiguration verlassen.

Die Anwendung verwendet eine Eigenschaft (external.baseUrl), die in der Anwendung.yml (unter src/test/resources) festgelegt ist. Ich habe jedoch keinen Weg gefunden, das programmgesteuert zu überschreiben. Ich habe so etwas ausprobiert:

    WireMockServer wireMockServer = new WireMockServer();
    wireMockServer.start();
    WireMock mockClient = new WireMock("localhost", wireMockServer.port());
    System.setProperty("external.baseUrl", "http://localhost:" + wireMockServer.port());

aber es hat nicht funktioniert und stattdessen wurde der Wert in application.yml verwendet. Alle anderen Lösungen, die ich mir angesehen habe, überschreiben die Eigenschaft mit einem statischen Wert (zum Beispiel in einer Anmerkung), aber ich kenne den Wert des Wiremock-Ports nicht, bis der Test ausgeführt wird.

Klarstellung:

Sowohl Spring Boot als auch Wiremock laufen an beliebigen Ports. Das ist in Ordnung und ich weiß, wie man den Wert beider Ports erhält. Wiremock soll jedoch einen externen Dienst verspotten, und ich muss meiner Anwendung mitteilen, wie sie zu erreichen ist. Ich mache das mit der external.baseUrl-Eigenschaft. Der Wert, den ich in meinem Test einstellen möchte, hängt natürlich von der Wiremock-Portnummer ab. Mein Problem ist einfach wie programmiert man eine Eigenschaft in einem Spring Boot Test .

5
bangnab

Erwägen Sie die Verwendung von Spring Cloud Contract Wiremock

Es gibt bereits einen Builder für JUnit-Regeln, mit dem Sie ${wiremock.port} Angeben können, um einen zufälligen Port in Eigenschafts-/Yaml-Dateien festzulegen

Oder Sie können WireMockRestServiceServer verwenden, um Wiremock an Ihre RestTemplate zu binden, sodass Sie in Ihren Tests nicht einmal URL überschreiben müssen

2
Dagon

Ich konnte keine Möglichkeit finden, Eigenschaften in einem Spring Boot-Integrationstest zu überschreiben, da der Test erst ausgeführt wird, nachdem die Anwendung erstellt und alle Beans bereits konfiguriert wurden.

Um dies zu umgehen, habe ich dem Test einen @TestConfiguration hinzugefügt, um die Beans in der Anwendung zu ersetzen:

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

Dadurch wurde der BeanUsingAProperty effektiv durch den im Test definierten ersetzt, sodass er die korrekte Portnummer für Wiremock hat.

Damit diese Konfiguration abgeholt werden kann, musste ich diese Klasse in die Testanmerkung einfügen

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

Beachten Sie, dass ich die nicht statische Wiremock-API verwende, da ich über mehrere solcher externen Dienste verfüge, die jeweils verspottet werden müssen. Beachten Sie, dass die Art und Weise, wie die verschiedenen Beans aufgebaut werden, je nach Design unterschiedlich ist.

5
bangnab

Verwenden Sie in Ihren application.properties die Eigenschaftssubstitution:

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

Dies erfordert, dass die wiremock.server.port-Eigenschaft festgelegt wird, bevor der SpringBootTest initialisiert wird. Dies kann durch Hinzufügen der @AutoConfigureWireMock-Anmerkung zu Ihrer Testklasse erreicht werden.

1
user3302637

Der in https://stackoverflow.com/a/48859553/30968 (d. H. wiremock.port) ist nicht korrekt, zumindest seit Spring Cloud Contract Version 2.1.2.RELEASE.

1. Arbeitsbeispiel

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. Andere WireMock-Eigenschaften

Außer wiremock.server.port, @AutoConfigureWireMock füllt die Umgebung auch mit einigen anderen Eigenschaften:

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

3. Gradle-Abhängigkeiten

Fügen Sie Ihrem Projekt die folgende Abhängigkeit hinzu, um Spring Cloud Contract WireMock in einem Gradle-basierten Projekt zu verwenden:

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

4. Verwendung in application.yaml Dateien

Wenn Sie Ihren Test konfigurieren application.yaml Datei wie folgt:

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

Und definieren Sie die folgenden 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();
    }
}

Sie können überprüfen, dass sample.port ist auf den zufällig ausgewählten Wiremock-Port eingestellt:

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

Der Ansatz, mit dem ich eine Eigenschaft beim Starten einer Spring Boot-App programmgesteuert ändere, besteht darin, den benutzerdefinierten Wert an die Haupteinstiegspunkte der Anwendung String[] Args zu übergeben. Dies hat zur Folge, dass alle anderen Mittel wie Systemeigenschaften, YML oder andere Konfigurationsdateien außer Kraft gesetzt werden.

Hier ist ein Beispiel :

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

Sie können auf einfache Weise eine statische Methode oder eine benutzerdefinierte API bereitstellen, mit der die Spring Boot-App (zum Testen) mit dem gewünschten Wert gestartet wird.

Und wenn Sie den Wert des Wiremock-Ports einmal erkannt haben, ist das ganz einfach. Hier ist ein Beispiel: PaymentServiceContractTest.Java

P.S. Karate (die Open-Source-Testbeispiele, die ich oben verwende) ist eine neue Alternative zu WireMock , probieren Sie es aus;)

0
Peter Thomas

Was ist mit diesem:

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