Using WireMock with Spring Boot 3 and JUnit 5

To use WireMock with JUnit 4.x you use a @Rule statement to configure the WireMock server:

@Rule
public WireMockRule wireMockRule = new WireMockRule();

With JUnit 5, @Rule was replaced with extensions, so the equivalent setup looks like this:

@RegisterExtension
static WireMockExtension wm1 = WireMockExtension.newInstance()
.options(options().port(8089))
.build();

Testing Spring Boot 3 apps with JUnit 5 however gives the following error as Spring Boot 3 does not have Jetty 11 dependencies, so WireMock’s use of Jetty 11 fails to start:

com.github.tomakehurst.wiremock.common.FatalStartupException: Jetty 11 is not present and no suitable HttpServerFactory extension was found. Please ensure that the classpath includes a WireMock extension that provides an HttpServerFactory implementation. See http://wiremock.org/docs/extending-wiremock/ for more information.
at com.github.tomakehurst.wiremock.WireMockServer.lambda$getHttpServerFactory$2(WireMockServer.java:95)

Per the WireMock docs here, wiremock-spring-boot provides support using another approach. To use, add this dependency:

<dependency>
<groupId>com.maciejwalkowiak.spring</groupId>
<artifactId>wiremock-spring-boot</artifactId>
<version>2.1.2</version>
<scope>test</scope>
</dependency>

Enable for your test by adding @EnableWireMock:

@SpringBootTest
@EnableWireMock({
@ConfigureWireMock(name = "your-mock-service", property = "your-url-to-mock.url")
})
class YourTest {
...
}

Inject the WireMock server into your test with:

@InjectWireMock("your-mock-service")
private WireMockServer wiremock;

@Value("${your-url-to-mock.url}")
private String wiremockUrl;

While this is a neat option to use with SpringBootTests, unfortunately it still doesn’t work with Spring Boot 3.3.x because of WireMock’s dependency on Jetty 11.

Tickets posted on the wiremock-spring-boot project suggest to avoid this in the meantime by using a dependency for wiremock-standalone instead:

        <dependency>
<groupId>com.maciejwalkowiak.spring</groupId>
<artifactId>wiremock-spring-boot</artifactId>
<version>2.0.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
<version>3.3.1</version>
<scope>test</scope>
</dependency>

This works as expected.

Jest Mocks with ES Modules

Jest’s support for mocks with CommonJS modules works great, they’re easy to use and it works great, as per the docs.

With ES Modules on the other hand, the docs are vague, and after spending several hours trying to get them to work with code I needed to test and not having any success, I spent a few more hours getting it to work with even the simplest example code.

This current issue describes multiple approaches which others have had varying success with, but there’s one tip mentioned that I can confirm is key to getting the mocks to work with ES modules and this is currently not mentioned in the docs (at least when I last looked) – you must await the import for the module you are mocking. This needs to be combined with the following:

  1. jest.unstable_mockModule() need to be called at the top level, and must be before the import for the module you are mocking
  2. You must await the import

To explain the last point, you must use this:

const { example2WithModule } = await import(‘./example.js’);

and not this:

import { example2WithModule } from ‘./example.js’;

Here’s an example of my working usage of jest.unstable_mockModule():

jest.unstable_mockModule('./example-module.js', () => ({
    exampleFunctionFromModule: jest.fn(
        () => {
            return 'mocked return!';
        }
    )
}));

node16 and Jest with ES6+ modules

I’ve run into this a few times, so leaving some notes for myself for next time,

By default, attempting to use “import x from ‘y’ ” style ES6 module imports with Jest will give you this error:

SyntaxError: Cannot use import statement outside a module

With node16, support for modules is added with the experiemental-vm option, so update your package.json to add a script executing jest with this flag:

"test": "node --experimental-vm-modules node_modules/.bin/jest"

and then also in your package.json, add:

  "type": "module",

If you search for the ‘cannot use import statement outside a module error there are plenty of recommendations, including more elaborate babel transpile ES6 modules to CommonJS style requires.

Depending what you’re doing maybe you’ll need some of the other approaches too, but I found the above 2 steps to be the bare minimum

Why do we Unit Test (and what’s the purpose of Code Coverage)?

What is Unit Testing? Unit Testing is the approach of testing the smallest parts of our code in isolation from other code. In Java these are methods in our classes. In Javascript these are our functions.

There are many unit testing frameworks that help us with our unit tests. In Java we have JUnit and TestNG, both have been around for a long time. The key part they help us with is making ‘assertions’ about the results of our code under test, to check the results and confirm when we call our code, it returns the expected results.

Why do we Unit Test? As developers we want to know our code works, that it does what it is supposed to do. We use unit tests to give us confidence about our code, so we know it works.

We can use unit tests to help with code changes or refactoring efforts to improve existing code. If existing tests are available, you can use them to check that they are showing you were the issue is that you need to fix (assuming there’s a test for that part of the code), or check that the existing code is working before your refactoring changes.

A typical flow for using unit tests to help with a defect fix looks like:

Run test, confirm test fails. Make code fix, confirm test passes. If the unit test for the related code doesn’t have an existing test for the code where the issue is, add a new test. Write a test for the expected result which should initially fail, rerun after the code fix and now the test should pass to confirm your fix is working.

Run other related tests in the same area to check there are no unexpected impacts to other existing code.

We can also use unit tests as part of a CI/CD pipeline – when our tests pass, the code is ready to include in a new build, and depending on your development process, ready for additional tests, integration testing, UAT testing and promotion into production.

Many industry studies have shown that it’s always cheaper to find and fix defects in our code earlier than later. The time invested to develop unit tests saves us in the long run if we can find issues earlier and avoid finding issues later, e.g. in production.

Any number of unit tests does not prove that our code is 100% correct however, neither does it prove the absence of bugs. If our unit tests are effective though, they can give us confidence that our code is working as expected for a range of tested inputs.

What is an effective test?

An effective unit test must have assertions to check and confirm the code when executed with given parameters returns the expected results. In JUnit, the framework provides a number of assert statements, like assertTrue(), assertEquals() that you use to confirm the expected results.

A unit test without assertions that only calls the code under test is not an effective test. Without assertions, the test is useless if you’re not checking the expected results.

Similarly for assertNotNull(). If the only thing your test does is check that there’s a non-null result, this doesn’t confirm that the code is returning the expected result, it only confirms a value is returned. This is not particularly useful when we expect a unit test to confirm that the code is returning the expected results (i.e. it is working).

For any given method that takes parameters, you should have a range of test methods to confirm the expected results for that range of input values. You should obviously test with expected/valid input values, but you should also test with a few unexpected/invalid values: high values, low values, empty, short and long strings. In all cases you should assert and confirm that the method returns the expected results.

How does Code Coverage relate to Unit Testing?

Code Coverage tells you how much of your code was execute by your tests. As a tool it is useful during the development of your unit tests, because it helps highlight where your tests may have missed paths through the code. For example, if the code you are testing has an if..then..else condition and your test calls the code with parameters that causes the if condition block to be tested but not the else block, Code Coverage helps highlight untested code.

The problem with Code Coverage though, is that by itself, Code Coverage is not an indication of effective tests or even working tests. If you have a test without an assertion that calls a method with a single block of lines of code and each of those lines executes, Code Coverage will report 100% coverage on this block. Without an assertion, this test doesn’t test that the code works, and therefore is worthless. It does have 100% Code Coverage though. This is why relying on Code Coverage alone is not useful, or sensible, because it doesn’t tell you how effective your tests are. It only tells you how much of the code was executed. Code that is executed by a test but is not confirmed if it is returning expected results or not with assertions, is just executed code, not working code.

Remember your assertions.

Use Code Coverage as a tool to help you find untested code, not as an ultimate goal: writing tests to achieve Code Coverage is not useful. Developing effective tests is your goal.