The context provides a tutorial on how to use WireMock with JUnit 5 in a Kotlin Spring Boot application.
Abstract
In the context, the author discusses the use of WireMock in integration testing to simulate server responses, allowing testing without calling actual web endpoints or starting a web server. As WireMock currently only supports JUnit 4, the author provides a workaround for using it with JUnit 5 by creating and registering a WireMock Bean and having Spring manage its lifecycle. The author also covers the pre-requisites, including knowledge of building a Kotlin Spring Boot application with Spring WebFlux and WireMock, and the use of JUnit 4 rules. The tutorial includes steps to write a WireMock Bean for JUnit 5 test, dynamically overwrite test property values, and migrate the test class to JUnit 5. The author also includes code snippets and explanations for each step.
Bullet points
WireMock is used to simulate server responses in integration testing.
WireMock currently only supports JUnit 4 and its rules.
The author provides a workaround for using WireMock with JUnit 5.
The tutorial covers creating and registering a WireMock Bean and having Spring manage its lifecycle.
Pre-requisites include knowledge of building a Kotlin Spring Boot application with Spring WebFlux and WireMock.
The tutorial includes steps to write a WireMock Bean for JUnit 5 test, dynamically overwrite test property values, and migrate the test class to JUnit 5.
Code snippets and explanations are included for each step.
How to Use WireMock with JUnit 5 in Kotlin Spring Boot Application
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 @Rule and @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.
Pre-requisite
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.
Otherwise, you can just clone the GitHub repo here to see the final code. If you wan to follow through the tutorial and write the code yourselves, clone this commit.
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 ApplicationContext and allows Beans to be @Autowired to 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 @ContextConfiguration ‘s initalizer parameter.
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 src/test/kotlin/io.codebrews.wiremockdemo folder.
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 build.gradle.kts:
org.junit.jupiter:junit-jupiter-api package for test implementation. JUnit Jupiter is JUnit 5 and junit-jupiter-api package is where the annotations are contained, e.g. @Test, @ExtendWith, @BeforeAll, @AfterAll.
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 stubFor and verify parts.
Alright, let’s go through some details now.
First, the @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 @ClassRule and @JvmField annotations. Since the WireMockServer is registered as a Spring Bean, we can just @Autowired it.
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 WireMockServer.
Lastly, we have to add the wireMockServer object to our stubFor and 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 stubFor and verify methods know which WireMock server is running and on which port. That’s why it was working fine.
Wrap Up
By the time you reach this section, you will have learned the following:
using Spring’s ApplicationContextInitializer<ConfigurableApplicationContext> to register a WireMockServer Bean 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. 🙂