It is a common practice to use WireMock in our Integration Test to simulate a server response. It allows us to complete our testing, without having to call the actual web endpoint or start a web server, which is awesome!
As of the time of this writing, WireMock does not support JUnit 5. You can see the discussion here. It currently supports JUnit 4’s
@ClassRule to manage its lifecycle automatically.
So, how can we use WireMock in a more seamless way for our Test Classes that use JUnit 5 and have Spring manages its lifecycle?
What You Will Learn
I am going to walkthrough how you can create and register a WireMock Bean and have Spring Application Context manages its lifecycle, just like if you were to use JUnit 4 rules.
If you want a complete walkthrough on how to build an application in Kotlin that uses Spring Boot and Spring Webflux along with WireMock for its Integration Test (JUnit 4), check out this article.
How to Build a Kotlin Spring Boot Application With Spring WebFlux and WireMock
Clean, modern Kotlin
WireMock in JUnit 4
This is how the Integration Test is currently written.
You can see that we use the following components in our test class:
@RunWith(SpringRunner::class): this provides the Spring
ApplicationContextand allows Beans to be
@Autowiredto your test instances.
@ClassRule: it runs the
before()method before the test cases start and the
after()method after all the test cases finish. It is used to set up things that are used by all the test cases and it applies to static method.
@JvmField: this annotation tells the Kotlin compiler to expose the annotated Kotlin object as a field in Java. Essentially, it sets the annotated method to be a static method.
All of these are from JUnit 4 and they allow us to let the WireMock server to be managed by Spring.
Write a WireMock Bean for JUnit 5 Test
Now, moving on to JUnit 5, we are going to create a Context Initializer Class that extends
ApplicationContextInitializer and provides the
WireMock Bean that will be managed by Spring to our test classes. We will then pass it to the
ApplicationContextInitializer can be used to initialise some configurable application context programmatically, for example, registering a Bean or Property sources. This means that for our WireMock Integration Test, we can programmatically register our WireMock Bean to the
ApplicationContext so then Spring will manage the lifecycle.
Alright, let’s go ahead and create a new file named
WireMockContextInitializer.kt. in our
What we have done is that we overwrite the
initialize() method which gets called during the initialisation of the
ApplicationContext. We define a
WireMockServer instance with a random port and calls its
start() method, which will spin up a WireMock server.
We need to call the
start() method explicitly, because our
WireMockServer Bean is being registered outside of the Spring IOC container and as such, the default mechanism of callbacks will not be invoked.
Next, we need to register the WireMock Bean to the Spring IOC container so we can
@Autowired it like a normal Bean. We register the Bean to the Bean Factory as a Singleton object. Now, our WireMock Bean is available in the Spring IOC container.
Finally, we add an
ApplicationListener which listens to events related to the
ApplicationContext. As for our use case, we want the WireMock server to shutdown when our tests are finished running.
ContextClosedEvent is the event that is raised upon the closing of the
ApplicationContext, which happens after our tests finish. So, this is why our
ApplicationListener is listening for that event and when it occurs, we call the
stop() method of
WireMockServer which will tear down the server.
Dynamically Overwrite Test Property Value
Last but not least, remember our
application.properties file, which contains the OpenWeather base url? It looks like this.
Recall that we set our
WireMockServer Bean to run on a random port. This means that our application will never hit the correct endpoint of the WireMock server. In other words, our tests will always fail. Now, this is a problem!
But, what if, somehow, we can dynamically overwrite the value of
app.openweather.baseurl at runtime based on the random port chosen by the WireMock server? Yes, that is possible. Let’s see how this can be done.
Isn’t this amazing? What’s going to happen is, Spring will set
app.openweather.baseurl to localhost with the port number coming from our
WireMockServer Bean. This means that our WireMock server will always run on an available port, so we can say goodbye to the
Address already in use exception. 💪
Migrate the Test Class to JUnit 5
Our custom Bean is ready and now, it’s time to implement it to our
RouteTest class, which is currently using JUnit 4. In this section, we will take a look at how to migrate to JUnit 5.
Add dependency to build.gradle.kts
To enable JUnit 5 testing, we need to add two things to our
org.junit.jupiter:junit-jupiter-apipackage for test implementation. JUnit Jupiter is JUnit 5 and
junit-jupiter-apipackage is where the annotations are contained, e.g.
useJUnitPlatform()to tell Gradle to use the JUnit Platform, which enables JUnit 5 for our tests.
Along with adding dependency, we’ll also upgrade the versions of Kotlin and Spring Boot framework.
Go ahead and rebuild the project using the Gradle build tool.
Modify the RouteTest.kt
As mentioned in the earlier section, our current
RouteTest.kt class uses annotations that are from JUnit 4. We need to remove and replace them all with annotations from JUnit 5. We also need to modify some of the WireMock related code a little bit, specifically the
Alright, let’s go through some details now.
@RunWith annotation is removed because it is not available in JUnit 5 anymore. Because we have a Context Initialiser class that provides the
WireMockServer Bean, we are going to add Spring’s
@ContextConfiguration to register the
WireMockServer Bean before our test cases run.
Secondly, we removed the
companion object along with the
@JvmField annotations. Since the
WireMockServer is registered as a Spring Bean, we can just
Thirdly, I have added an
@AfterEach method to reset the
WireMockServer after each of the test cases in terms of its stub responses. Right now, it is actually not necessary as we only have one test case that really uses
WireMockServer. I only put it there so you know what to do when you have multiple test cases that rely on the
Lastly, we have to add the
wireMockServer object to our
verify methods or else we would get the following error.
org.apache.http.conn.HttpHostConnectException: Connect to localhost:8080 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused (Connection refused)
Essentially, we need to add the specific instance of
WireMockServer that is actually running. Previously when using
WireMockRule, it actually registers the WireMock server instance so then, the
verify methods know which WireMock server is running and on which port. That’s why it was working fine.
By the time you reach this section, you will have learned the following:
- using Spring’s
ApplicationContextInitializer<ConfigurableApplicationContext>to register a
WireMockServerBean to the Spring IOC during our Integration Test
- migrating from JUnit 4 based test class to JUnit 5
I hope it helps and inspires you to explore more on WireMock and JUnit 5. 🙂