温馨提示:本文翻译自stackoverflow.com,查看原文请点击:junit5 - Wiremock with Spring Boot JUnit 5 test: Address in Use after test run
spring-boot junit5 wiremock

junit5 - 带有Spring Boot JUnit 5测试的Wiremock: Address in Use after test run

发布于 2020-03-29 21:40:50

我们有一个Spring Boot 2.2.0.RELEASE应用程序,正在使用WireMock与JUnit 5测试类进行测试。该测试在本地运行良好,但是在我们的Jenkins上,在测试成功运行之后,它失败并显示“ Address in using”(地址已在使用中)消息

这是来自的Spring依赖项pom.xml

<properties>
    <java.version>11</java.version>
    <spring-cloud.version>Hoxton.RC2</spring-cloud.version>
    <spring-cloud-stream.version>3.0.0.RC2</spring-cloud-stream.version>
    <openapi.codegen.maven.plugin.version>4.1.2</openapi.codegen.maven.plugin.version>
    <jacoco-maven-plugin.version>0.8.4</jacoco-maven-plugin.version>
</properties>

<dependencies>
    <!-- Spring -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-stream</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-kafka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>

    <!-- Utils -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.openapitools</groupId>
        <artifactId>openapi-generator</artifactId>
        <version>${openapi.codegen.maven.plugin.version}</version>
    </dependency>

    <!-- Testing -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-contract-wiremock</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-stream</artifactId>
        <version>${spring-cloud-stream.version}</version>
        <type>test-jar</type>
        <scope>test</scope>
        <classifier>test-binder</classifier>
    </dependency>
</dependencies>

所以我们的测试非常简单,看起来像这样:

@ExtendWith(SpringExtension.class)
@SpringBootTest
@ActiveProfiles("test")
@AutoConfigureWireMock(port = 0)
@Import(TestChannelBinderConfiguration.class)
class OurTestClass {
    @Autowired
    private OurDataCache cache;
    @Autowired
    private InputDestination source;
    @Autowired
    private OutputDestination target;

    @BeforeEach
    void setupApi() throws IOException, URISyntaxException {
        stubFor(get("/endpoint")
                .willReturn(aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
                        .withBody(data())
                )
        );
    }

    @Test
    void sampleTest() {
        String messageContent = loadResourceFileAsMessage("messageIn.json");
        String expectedOutMessage = loadResourceFileAsMessage("messageOut.json");

        Message<byte[]> message = new GenericMessage<>(messageContent.getBytes());

        source.send(message);

        Message<byte[]> received = target.receive();
        assertThat(received, notNullValue());

        assertThat(new String(received.getPayload()), equalTo(expectedOutMessage.replace(" ", "")));
    }
}

再次,这在本地运行良好,并且在Jenkins上,实际的测试用例通过了,但是随后出现错误:

10:29:40  2019-11-25 09:29:40.683  WARN 414 --- [           main] o.s.test.context.TestContextManager      : Caught exception while invoking 'afterTestClass' callback on TestExecutionListener [org.springframework.cloud.contract.wiremock.WireMockTestExecutionListener@2b68c59b] for test class [class our.test.Class]
10:29:40
10:29:40  com.github.tomakehurst.wiremock.common.FatalStartupException: java.lang.RuntimeException: java.io.IOException: Failed to bind to /0.0.0.0:12193
10:29:40    at com.github.tomakehurst.wiremock.WireMockServer.start(WireMockServer.java:148) ~[wiremock-jre8-standalone-2.25.1.jar:na]
10:29:40    at org.springframework.cloud.contract.wiremock.WireMockConfiguration.reRegisterServer(WireMockConfiguration.java:137) ~[spring-cloud-contract-wiremock-2.2.0.RC2.jar:2.2.0.RC2]
10:29:40    at org.springframework.cloud.contract.wiremock.WireMockConfiguration.resetMappings(WireMockConfiguration.java:150) ~[spring-cloud-contract-wiremock-2.2.0.RC2.jar:2.2.0.RC2]
10:29:40    at org.springframework.cloud.contract.wiremock.WireMockTestExecutionListener.afterTestClass(WireMockTestExecutionListener.java:76) ~[spring-cloud-contract-wiremock-2.2.0.RC2.jar:2.2.0.RC2]
10:29:40    at org.springframework.test.context.TestContextManager.afterTestClass(TestContextManager.java:488) ~[spring-test-5.2.0.RELEASE.jar:5.2.0.RELEASE]
10:29:40    at org.springframework.test.context.junit.jupiter.SpringExtension.afterAll(SpringExtension.java:86) ~[spring-test-5.2.0.RELEASE.jar:5.2.0.RELEASE]
10:29:40    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeAfterAllCallbacks$13(ClassBasedTestDescriptor.java:421) ~[junit-jupiter-engine-5.5.2.jar:5.5.2]
10:29:40    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.5.2.jar:1.5.2]
10:29:40    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeAfterAllCallbacks$14(ClassBasedTestDescriptor.java:421) ~[junit-jupiter-engine-5.5.2.jar:5.5.2]
10:29:40    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) ~[na:na]
10:29:40    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeAfterAllCallbacks(ClassBasedTestDescriptor.java:421) ~[junit-jupiter-engine-5.5.2.jar:5.5.2]
[...]
10:29:40    at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:126) ~[surefire-booter-2.22.2.jar:2.22.2]
10:29:40    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418) ~[surefire-booter-2.22.2.jar:2.22.2]
10:29:40  Caused by: java.lang.RuntimeException: java.io.IOException: Failed to bind to /0.0.0.0:12193
10:29:40    at com.github.tomakehurst.wiremock.jetty9.JettyHttpServer.start(JettyHttpServer.java:184) ~[wiremock-jre8-standalone-2.25.1.jar:na]
10:29:40    at com.github.tomakehurst.wiremock.WireMockServer.start(WireMockServer.java:146) ~[wiremock-jre8-standalone-2.25.1.jar:na]
10:29:40    ... 44 common frames omitted
10:29:40  Caused by: java.io.IOException: Failed to bind to /0.0.0.0:12193
10:29:40    at wiremock.org.eclipse.jetty.server.ServerConnector.openAcceptChannel(ServerConnector.java:346) ~[wiremock-jre8-standalone-2.25.1.jar:na]
[...]
10:29:40    at com.github.tomakehurst.wiremock.jetty9.JettyHttpServer.start(JettyHttpServer.java:182) ~[wiremock-jre8-standalone-2.25.1.jar:na]
10:29:40    ... 45 common frames omitted
10:29:40  Caused by: java.net.BindException: Address already in use
10:29:40    at java.base/sun.nio.ch.Net.bind0(Native Method) ~[na:na]
10:29:40    at java.base/sun.nio.ch.Net.bind(Net.java:461) ~[na:na]
10:29:40    at java.base/sun.nio.ch.Net.bind(Net.java:453) ~[na:na]
10:29:40    at java.base/sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:227) ~[na:na]
10:29:40    at java.base/sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:80) ~[na:na]
10:29:40    at wiremock.org.eclipse.jetty.server.ServerConnector.openAcceptChannel(ServerConnector.java:342) ~[wiremock-jre8-standalone-2.25.1.jar:na]
10:29:40    ... 52 common frames omitted
10:29:40
10:29:40  [ERROR] [1;31mTests [0;1mrun: [0;1m2[m, Failures: 0, [1;31mErrors: [0;1;31m1[m, Skipped: 0, Time elapsed: 15.925 s[1;31m <<< FAILURE!
10:29:40  [ERROR] our.test.Class Time elapsed: 1.843 s  <<< ERROR!
10:29:40  com.github.tomakehurst.wiremock.common.FatalStartupException: java.lang.RuntimeException: java.io.IOException: Failed to bind to /0.0.0.0:12193
10:29:40  Caused by: java.lang.RuntimeException: java.io.IOException: Failed to bind to /0.0.0.0:12193
10:29:40  Caused by: java.io.IOException: Failed to bind to /0.0.0.0:12193
10:29:40  Caused by: java.net.BindException: Address already in use

我还为Spring启用了DEBUG日志记录WireMockConfiguration,很有趣的是,构建成功。它确实提到了“在下一个测试中重置映射以重新启动它们。当在随机端口上运行的新服务器重新使用相同上下文时,这是必要的”。

这使我认为这可能是某种竞赛情况,但是我不能说我完全掌握了整体设置。

任何指针都会有所帮助。

查看更多

提问者
daniu
被浏览
173
Mateusz Rasiński 2020-01-31 18:26

这是一个已知问题:https : //github.com/spring-cloud/spring-cloud-contract/issues/665

您必须@DirtiesContext在所有使用WireMock或spring.test.context.cache.maxSize=1src / test / resources / spring.propertieshttps://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext中进行设置的测试中使用-ctx-management-caching