Skip to content

WireMock servers not shutting down before next test, when new ApplicationContext is triggered #82

Open
@iranicus

Description

@iranicus

Proposal

spring boot version: 3.3.7
spring boot test version: 3.3.7
wiremock-spring-boot version: 3.3.0 (tried all newer and older versions too)

Currently mocking one service we call multiple times across different integration tests on the same port.

Once one of the integration tests introduces @SpyBean, there is a change in wiremock behavior in that the wiremock server is not stopped once all the test methods have been executed in the class. Currently this results in a port conflict with the next integration test class which tries to start up wiremock on the same port:

java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@1efc6a80 testClass = com.uk.IntegrationTestTwo, locations = [], classes = [com.uk.Application], contextInitializerClasses = [], activeProfiles = [], propertySourceDescriptors = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@7776ab, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@2f1de2d6, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@1941a8ff, org.springframework.boot.test.web.reactor.netty.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory$DisableReactorResourceFactoryGlobalResourcesContextCustomizerCustomizer@2250b9f2, org.springframework.boot.test.autoconfigure.OnFailureConditionReportContextCustomizerFactory$OnFailureConditionReportContextCustomizer@142269f2, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@6986bbaf, org.wiremock.spring.internal.WireMockContextCustomizer@c93d9c4f, org.springframework.boot.test.context.SpringBootTestAnnotation@74ebcb4a], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]

	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:180)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130)
	at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191)
	at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260)
	at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:163)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
	at java.base/java.util.Optional.orElseGet(Optional.java:364)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: com.github.tomakehurst.wiremock.common.FatalStartupException: com.github.tomakehurst.wiremock.common.FatalStartupException: java.io.IOException: Failed to bind to /0.0.0.0:8141
	at com.github.tomakehurst.wiremock.WireMockServer.start(WireMockServer.java:171)
	at org.wiremock.spring.internal.WireMockServerCreator.createWireMockServer(WireMockServerCreator.java:94)
	at org.wiremock.spring.internal.WireMockContextCustomizer.resolveOrCreateWireMockServer(WireMockContextCustomizer.java:57)
	at org.wiremock.spring.internal.WireMockContextCustomizer.customizeContext(WireMockContextCustomizer.java:47)
	at org.springframework.boot.test.context.SpringBootContextLoader$ContextCustomizerAdapter.initialize(SpringBootContextLoader.java:443)
	at org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:627)
	at org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:401)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:334)
	at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137)
	at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
	at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
	at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1463)
	at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152)
	... 17 more
Caused by: com.github.tomakehurst.wiremock.common.FatalStartupException: java.io.IOException: Failed to bind to /0.0.0.0:8141
	at com.github.tomakehurst.wiremock.jetty.JettyHttpServer.start(JettyHttpServer.java:149)
	at com.github.tomakehurst.wiremock.WireMockServer.start(WireMockServer.java:169)
	... 33 more
Caused by: java.io.IOException: Failed to bind to /0.0.0.0:8141
	at org.eclipse.jetty.server.ServerConnector.openAcceptChannel(ServerConnector.java:349)
	at org.eclipse.jetty.server.ServerConnector.open(ServerConnector.java:313)
	at org.eclipse.jetty.server.Server.lambda$doStart$0(Server.java:569)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at org.eclipse.jetty.server.Server.doStart(Server.java:565)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93)
	at com.github.tomakehurst.wiremock.jetty.JettyHttpServer.start(JettyHttpServer.java:144)
	... 34 more
Caused by: java.net.BindException: Address already in use
	at java.base/sun.nio.ch.Net.bind0(Native Method)
	at java.base/sun.nio.ch.Net.bind(Net.java:555)
	at java.base/sun.nio.ch.ServerSocketChannelImpl.netBind(ServerSocketChannelImpl.java:337)
	at java.base/sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:294)

Expectation is wiremock server would use normal behavior with stoping once all test methods in a test class are completed so that the next test class can start another wiremock server up on the same port. I can confirm that without @SpyBean being used that this happens as expected.

Reproduction steps

spring boot version 3.3.7
any wiremock-spring-boot version

Run multiple @SpringBootTest integration test classes (not ran in parallel) that mock the same port with a spring-boot-test @SpyBean in at least one of them:

ServiceA

package com.uk;

import org.springframework.stereotype.Component;

@Component
public class ServiceA {
}

Integration Test Class 1 example

package com.uk;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.wiremock.spring.ConfigureWireMock;
import org.wiremock.spring.EnableWireMock;
import com.uk.ServiceA;

@SpringBootTest
@EnableWireMock({@ConfigureWireMock(name = "service", port = 8141)})
class IntegrationTestOne {
  @SpyBean private ServiceA serviceA;

  @Test
  void testWiremock() {
    assertThat(1 + 2).isEqualTo(3);
  }
}

Integration Test Class 2 example

package com.uk;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.wiremock.spring.ConfigureWireMock;
import org.wiremock.spring.EnableWireMock;

@SpringBootTest
@EnableWireMock({@ConfigureWireMock(name = "service", port = 8141)})
class IntegrationTestTwo {

  @Test
  void testWiremock() {
    assertThat(3 + 1).isEqualTo(4);
  }
}

References

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    4.xxbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions