我们有一个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
,很有趣的是,构建成功。它确实提到了“在下一个测试中重置映射以重新启动它们。当在随机端口上运行的新服务器重新使用相同上下文时,这是必要的”。
这使我认为这可能是某种竞赛情况,但是我不能说我完全掌握了整体设置。
任何指针都会有所帮助。
这是一个已知问题:https : //github.com/spring-cloud/spring-cloud-contract/issues/665
您必须@DirtiesContext
在所有使用WireMock或spring.test.context.cache.maxSize=1
在src / test / resources / spring.properties(https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext中进行设置的测试中使用-ctx-management-caching)
这很奇怪,因为该问题已解决,我们正在使用一个应该修复的版本之后的版本:/
嗯,它不再固定,因为不再有问题。它是固定的,因为我们知道这个问题,但解决的办法就是添加
@DirtiesContext
。参见:github.com/spring-cloud/spring-cloud-contract/issues/…嗯,这不是解决方案,而是一种解决方法。“我们知道这个问题,所以已解决 ”是一种有点奇怪的方法,我实际上可以用它关闭每张票。