Mocking and Stubbing External REST APIs, Part Two

After we have finished this webinar, we

  • Can get the required dependencies with Maven and Gradle.
  • Know how we can mock and stub external REST APIs with WireMock.
  • Know when we should use WireMock.

Getting the Required Dependencies

Before we can use WireMock in our integration tests, we have to declare the WireMock dependency in our build script.

If we are using Maven, we have to add the following snippet to our pom.xml file:

1<dependency>
2	<groupId>com.github.tomakehurst</groupId>
3	<artifactId>wiremock</artifactId>
4	<version>2.6.0</version>
5	<scope>test</scope>
6</dependency>

If we are using Gradle, we have to add the following snippet to our build.gradle file:

1integrationTestCompile (
2		'com.github.tomakehurst:wiremock:2.6.0'
3)

Let’s move on and find out how we can configure WireMock.

Configuring WireMock

During the previous webinar , we replaced the VatPercentageService bean with a test double when we run our integration tests. We did this because this class obtains the vat percentage of a country by sending a GET request to an external REST API, and we wanted to eliminate the dependency between our integration tests and the external REST API.

The source code of the VatPercentageService class looks as follows:

 1import org.springframework.stereotype.Service;
 2import org.springframework.web.client.RestTemplate;
 3
 4@Service
 5public class VatPercentageService {
 6
 7    private final RestTemplate restTemplate;
 8
 9    public VatPercentageService(RestTemplate restTemplate) {
10        this.restTemplate = restTemplate;
11    }
12
13    public VatPercentage findByCountryCode(String countryCode) {
14        return restTemplate.getForObject(String.format(
15                "http://localhost:8080/api/external/vat-percentage?countryCode=%s",
16                countryCode
17            ),
18            VatPercentage.class
19        );
20    }
21}

When we replaced the VatPercentageService bean with a stub, we wrote three integration tests which verify that the tested controller is working as expected. After we have removed all Mockito specific code from our test class, its source code looks as follows:

 1import org.junit.Test;
 2import org.junit.experimental.categories.Category;
 3import org.junit.runner.RunWith;
 4import org.springframework.beans.factory.annotation.Autowired;
 5import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 6import org.springframework.boot.test.context.SpringBootTest;
 7import org.springframework.http.MediaType;
 8import org.springframework.test.context.ActiveProfiles;
 9import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
10import org.springframework.test.web.servlet.MockMvc;
11import org.springframework.test.web.servlet.ResultActions;
12
13import static org.hamcrest.Matchers.is;
14import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
15import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
16import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
17import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
18
19@RunWith(SpringJUnit4ClassRunner.class)
20@SpringBootTest(classes = {ExampleApplication.class})
21@AutoConfigureMockMvc
22@Category(IntegrationTest.class)
23@ActiveProfiles(Profiles.INTEGRATION_TEST)
24public class FindVatPercentageStubTest {
25
26    private static final String COUNTRY_CODE = "FI";
27    private static final Integer VAT_PERCENTAGE = 24;
28
29    @Autowired
30    private MockMvc mockMvc;
31
32    @Test
33    public void shouldReturnHttpStatusCodeOk() throws Exception {
34        findByCountryCode(COUNTRY_CODE)
35                .andExpect(status().isOk());
36    }
37
38    @Test
39    public void shouldReturnVatPercentageAsJson() throws Exception {
40        findByCountryCode(COUNTRY_CODE)
41                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
42    }
43
44    @Test
45    public void shouldReturnCorrectVatPercentage() throws Exception {
46        findByCountryCode(COUNTRY_CODE)
47                .andExpect(jsonPath("$.countryCode", is(COUNTRY_CODE)))
48                .andExpect(jsonPath("$.vatPercentage", is(VAT_PERCENTAGE)));
49    }
50
51    private ResultActions findByCountryCode(String countryCode) throws Exception {
52        return mockMvc.perform(get("/api/vat-percentage")
53                .param("countryCode", countryCode)
54        );
55    }
56}

We can configure WireMock by using a custom JUnit 4 rule called: WireMockRule that starts the WireMock server before a test method is run and stops it after a test method has been run. We can configure the WireMock server by following these steps:

  1. Add a WireMockRule field to our test class and annotate that field with the @Rule annotation.
  2. Configure the started WireMock server. When we create a new WireMockRule object, we can configure the started server by passing a new Options object to the constructor of the WireMockRule class. When we configure the WireMock server, we have to ensure that it listens to the host: localhost and the port: 8080.

After we have configured WireMock, the source code of our test class looks as follows:

 1import com.github.tomakehurst.wiremock.junit.WireMockRule;
 2import org.junit.Rule;
 3import org.junit.Test;
 4import org.junit.experimental.categories.Category;
 5import org.junit.runner.RunWith;
 6import org.springframework.beans.factory.annotation.Autowired;
 7import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 8import org.springframework.boot.test.context.SpringBootTest;
 9import org.springframework.http.MediaType;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12import org.springframework.test.web.servlet.MockMvc;
13import org.springframework.test.web.servlet.ResultActions;
14
15import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
16import static org.hamcrest.Matchers.is;
17import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
18import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
19import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
20import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
21
22@RunWith(SpringJUnit4ClassRunner.class)
23@SpringBootTest(classes = {ExampleApplication.class})
24@AutoConfigureMockMvc
25@Category(IntegrationTest.class)
26@ActiveProfiles(Profiles.INTEGRATION_TEST)
27public class FindVatPercentageStubTest {
28
29    private static final String COUNTRY_CODE = "FI";
30    private static final Integer VAT_PERCENTAGE = 24;
31
32    @Rule
33    public WireMockRule wireMockRule = new WireMockRule(options()
34            .bindAddress("localhost")
35            .port(8080)
36    );
37
38    @Autowired
39    private MockMvc mockMvc;
40
41    @Test
42    public void shouldReturnHttpStatusCodeOk() throws Exception {
43        findByCountryCode(COUNTRY_CODE)
44                .andExpect(status().isOk());
45    }
46
47    @Test
48    public void shouldReturnVatPercentageAsJson() throws Exception {
49        findByCountryCode(COUNTRY_CODE)
50                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
51    }
52
53    @Test
54    public void shouldReturnCorrectVatPercentage() throws Exception {
55        findByCountryCode(COUNTRY_CODE)
56                .andExpect(jsonPath("$.countryCode", is(COUNTRY_CODE)))
57                .andExpect(jsonPath("$.vatPercentage", is(VAT_PERCENTAGE)));
58    }
59
60    private ResultActions findByCountryCode(String countryCode) throws Exception {
61        return mockMvc.perform(get("/api/vat-percentage")
62                .param("countryCode", countryCode)
63        );
64    }
65}
We don’t have to provide additional configuration to the WireMock server because the host name of our external REST API is localhost, and it listens to the port: 8080. However, I wanted to show you how you can provide additional configuration to the started WireMock server.

Next, we will find out how we can configure the returned HTTP response with WireMock.

Configuring the Returned HTTP Response

Our integration tests assume that the external REST API returns the vat percentage 24 when it is invoked by using the country code: ‘FI’. We can configure the returned response by following these steps:

  1. Create a setup method.
  2. Invoke the givenThat() method of the WireMock class. This method allows us to configure the returned HTTP response.
  3. Configure the expected request method (GET) and the expected request URL (/api/external/vat-percentage?countryCode=FI). By the way, when we configure the expected request URL, we don’t have to specify the protocol, host name, or the used port.
  4. Configure the returned HTTP status code (200) and the returned JSON document.

After we have configured the returned response, the source code of our integration test class looks as follows:

 1import com.github.tomakehurst.wiremock.client.WireMock;
 2import com.github.tomakehurst.wiremock.junit.WireMockRule;
 3import org.junit.Before;
 4import org.junit.Rule;
 5import org.junit.Test;
 6import org.junit.experimental.categories.Category;
 7import org.junit.runner.RunWith;
 8import org.springframework.beans.factory.annotation.Autowired;
 9import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
10import org.springframework.boot.test.context.SpringBootTest;
11import org.springframework.http.MediaType;
12import org.springframework.test.context.ActiveProfiles;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.web.servlet.MockMvc;
15import org.springframework.test.web.servlet.ResultActions;
16
17import static com.github.tomakehurst.wiremock.client.WireMock.*;
18import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
19import static org.hamcrest.Matchers.is;
20import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
21import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
22import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
23import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
24
25@RunWith(SpringJUnit4ClassRunner.class)
26@SpringBootTest(classes = {ExampleApplication.class})
27@AutoConfigureMockMvc
28@Category(IntegrationTest.class)
29@ActiveProfiles(Profiles.INTEGRATION_TEST)
30public class FindVatPercentageStubTest {
31
32    private static final String COUNTRY_CODE = "FI";
33    private static final Integer VAT_PERCENTAGE = 24;
34
35    @Rule
36    public WireMockRule wireMockRule = new WireMockRule(options()
37            .bindAddress("localhost")
38            .port(8080)
39    );
40
41    @Autowired
42    private MockMvc mockMvc;
43
44    @Before
45    public void returnVatPercentage() {
46        givenThat(WireMock.get(
47                urlEqualTo("/api/external/vat-percentage?countryCode=FI")
48        )
49                .willReturn(aResponse()
50                        .withStatus(200)
51                        .withHeader("Content-Type",
52                                "application/json;charset=UTF-8"
53                        )
54                        .withBody("{" +
55                                "\"countryCode\": \"FI\"," +
56                                "\"vatPercentage\": 24" +
57                        "}")
58                )
59        );
60    }
61
62    @Test
63    public void shouldReturnHttpStatusCodeOk() throws Exception {
64        findByCountryCode(COUNTRY_CODE)
65                .andExpect(status().isOk());
66    }
67
68    @Test
69    public void shouldReturnVatPercentageAsJson() throws Exception {
70        findByCountryCode(COUNTRY_CODE)
71                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
72    }
73
74    @Test
75    public void shouldReturnCorrectVatPercentage() throws Exception {
76        findByCountryCode(COUNTRY_CODE)
77                .andExpect(jsonPath("$.countryCode", is(COUNTRY_CODE)))
78                .andExpect(jsonPath("$.vatPercentage", is(VAT_PERCENTAGE)));
79    }
80
81    private ResultActions findByCountryCode(String countryCode) throws Exception {
82        return mockMvc.perform(get("/api/vat-percentage")
83                .param("countryCode", countryCode)
84        );
85    }
86}

Even though this works, it’s a bit too long for my taste. We can make it shorter by following these steps:

First, if the expected HTTP request has multiple request parameters, the expected URL will become quite long. That’s why we have to specify the expected HTTP request by following these steps:

  1. Ignore the request parameter when we configure the expected URL address.
  2. Ensure that the value of the countryCode request parameter is equal to: ‘FI’.

After we have specified the expected HTTP request, the source code of our test class looks as follows:

 1import com.github.tomakehurst.wiremock.client.WireMock;
 2import com.github.tomakehurst.wiremock.junit.WireMockRule;
 3import org.junit.Before;
 4import org.junit.Rule;
 5import org.junit.Test;
 6import org.junit.experimental.categories.Category;
 7import org.junit.runner.RunWith;
 8import org.springframework.beans.factory.annotation.Autowired;
 9import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
10import org.springframework.boot.test.context.SpringBootTest;
11import org.springframework.http.MediaType;
12import org.springframework.test.context.ActiveProfiles;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.web.servlet.MockMvc;
15import org.springframework.test.web.servlet.ResultActions;
16
17import static com.github.tomakehurst.wiremock.client.WireMock.*;
18import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
19import static org.hamcrest.Matchers.is;
20import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
21import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
22import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
23import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
24
25@RunWith(SpringJUnit4ClassRunner.class)
26@SpringBootTest(classes = {ExampleApplication.class})
27@AutoConfigureMockMvc
28@Category(IntegrationTest.class)
29@ActiveProfiles(Profiles.INTEGRATION_TEST)
30public class FindVatPercentageStubTest {
31
32    private static final String COUNTRY_CODE = "FI";
33    private static final Integer VAT_PERCENTAGE = 24;
34
35    @Rule
36    public WireMockRule wireMockRule = new WireMockRule(options()
37            .bindAddress("localhost")
38            .port(8080)
39    );
40
41    @Autowired
42    private MockMvc mockMvc;
43
44    @Before
45    public void returnVatPercentage() {
46        givenThat(WireMock.get(
47                urlMatching("/api/external/vat-percentage.+")
48        )
49                        .withQueryParam("countryCode", equalTo("FI"))
50                        .willReturn(aResponse()
51                                .withStatus(200)
52                                .withHeader("Content-Type",
53                                        "application/json;charset=UTF-8"
54                                )
55                                .withBody("{" +
56                                        "\"countryCode\": \"FI\"," +
57                                        "\"vatPercentage\": 24" +
58                                "}")
59                        )
60        );
61    }
62
63    @Test
64    public void shouldReturnHttpStatusCodeOk() throws Exception {
65        findByCountryCode(COUNTRY_CODE)
66                .andExpect(status().isOk());
67    }
68
69    @Test
70    public void shouldReturnVatPercentageAsJson() throws Exception {
71        findByCountryCode(COUNTRY_CODE)
72                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
73    }
74
75    @Test
76    public void shouldReturnCorrectVatPercentage() throws Exception {
77        findByCountryCode(COUNTRY_CODE)
78                .andExpect(jsonPath("$.countryCode", is(COUNTRY_CODE)))
79                .andExpect(jsonPath("$.vatPercentage", is(VAT_PERCENTAGE)));
80    }
81
82    private ResultActions findByCountryCode(String countryCode) throws Exception {
83        return mockMvc.perform(get("/api/vat-percentage")
84                .param("countryCode", countryCode)
85        );
86    }
87}

Second, we have to configure the returned JSON document by using the the okJson() method of the WireMock class. This method takes the returned JSON document as a method parameter and returns the HTTP status code 200.

After we have configured the returned JSON document, the source code of our test class looks as follows:

 1import com.github.tomakehurst.wiremock.client.WireMock;
 2import com.github.tomakehurst.wiremock.junit.WireMockRule;
 3import org.junit.Before;
 4import org.junit.Rule;
 5import org.junit.Test;
 6import org.junit.experimental.categories.Category;
 7import org.junit.runner.RunWith;
 8import org.springframework.beans.factory.annotation.Autowired;
 9import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
10import org.springframework.boot.test.context.SpringBootTest;
11import org.springframework.http.MediaType;
12import org.springframework.test.context.ActiveProfiles;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.web.servlet.MockMvc;
15import org.springframework.test.web.servlet.ResultActions;
16
17import static com.github.tomakehurst.wiremock.client.WireMock.*;
18import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
19import static org.hamcrest.Matchers.is;
20import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
21import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
22import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
23import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
24
25@RunWith(SpringJUnit4ClassRunner.class)
26@SpringBootTest(classes = {ExampleApplication.class})
27@AutoConfigureMockMvc
28@Category(IntegrationTest.class)
29@ActiveProfiles(Profiles.INTEGRATION_TEST)
30public class FindVatPercentageStubTest {
31
32    private static final String COUNTRY_CODE = "FI";
33    private static final Integer VAT_PERCENTAGE = 24;
34
35    @Rule
36    public WireMockRule wireMockRule = new WireMockRule(options()
37            .bindAddress("localhost")
38            .port(8080)
39    );
40
41    @Autowired
42    private MockMvc mockMvc;
43
44    @Before
45    public void returnVatPercentage() {
46        givenThat(WireMock.get(
47                urlMatching("/api/external/vat-percentage.+")
48                )
49                        .withQueryParam("countryCode", equalTo("FI"))
50                        .willReturn(okJson("{" +
51                                "\"countryCode\": \"FI\"," +
52                                "\"vatPercentage\": 24" +
53                        "}"))
54        );
55    }
56
57    @Test
58    public void shouldReturnHttpStatusCodeOk() throws Exception {
59        findByCountryCode(COUNTRY_CODE)
60                .andExpect(status().isOk());
61    }
62
63    @Test
64    public void shouldReturnVatPercentageAsJson() throws Exception {
65        findByCountryCode(COUNTRY_CODE)
66                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
67    }
68
69    @Test
70    public void shouldReturnCorrectVatPercentage() throws Exception {
71        findByCountryCode(COUNTRY_CODE)
72                .andExpect(jsonPath("$.countryCode", is(COUNTRY_CODE)))
73                .andExpect(jsonPath("$.vatPercentage", is(VAT_PERCENTAGE)));
74    }
75
76    private ResultActions findByCountryCode(String countryCode) throws Exception {
77        return mockMvc.perform(get("/api/vat-percentage")
78                .param("countryCode", countryCode)
79        );
80    }
81}

WireMock has a quite versatile support for stubbing. Unfortunately we can only scratch the surface of that support because we don’t have time to cover all scenarios. That is why I recommend that you should take a look at the following documentation:

Additional Reading:

We have now learned how we can configure the returned HTTP response with WireMock. Let’s move on and find out how we can verify that the WireMock server received a specific HTTP request.

Verifying the Received HTTP Requests

If we want to verify that our application sent an HTTP request to the external API, we have to follow these steps:

First, we have to override the default response of WireMock that fails our integration tests if WireMock cannot find a configured response for an HTTP request. We can do this by following these steps:

  1. Implement a setup method.
  2. Ensure that WireMock returns the HTTP status code 200 for all unmapped HTTP requests.

After we have overridden the default HTTP response, the source code of our test class looks as follows:

 1import com.github.tomakehurst.wiremock.junit.WireMockRule;
 2import org.junit.Before;
 3import org.junit.Rule;
 4import org.junit.experimental.categories.Category;
 5import org.junit.runner.RunWith;
 6import org.springframework.beans.factory.annotation.Autowired;
 7import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 8import org.springframework.boot.test.context.SpringBootTest;
 9import org.springframework.test.context.ActiveProfiles;
10import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11import org.springframework.test.web.servlet.MockMvc;
12import org.springframework.test.web.servlet.ResultActions;
13
14import static com.github.tomakehurst.wiremock.client.WireMock.*;
15import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
16import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
17
18@RunWith(SpringJUnit4ClassRunner.class)
19@SpringBootTest(classes = {ExampleApplication.class})
20@AutoConfigureMockMvc
21@Category(IntegrationTest.class)
22@ActiveProfiles(Profiles.INTEGRATION_TEST)
23public class FindVatPercentageMockTest {
24
25    private static final String COUNTRY_CODE = "FI";
26
27    @Rule
28    public WireMockRule wireMockRule = new WireMockRule(options()
29            .bindAddress("localhost")
30            .port(8080)
31    );
32
33    @Autowired
34    private MockMvc mockMvc;
35
36    @Before
37    public void configureDefaultResponse() {
38        givenThat(any(anyUrl())
39                .willReturn(ok())
40        );
41    }
42
43    private ResultActions findByCountryCode(String countryCode) throws Exception {
44        return mockMvc.perform(get("/api/vat-percentage")
45                .param("countryCode", countryCode)
46        );
47    }
48}

Second, we have to write our test method by following these steps:

  1. Create a new test method.
  2. Send a GET request to the tested controller method and configure the value of the countryCode request parameter.
  3. Verify that WireMock received one GET request and configure the URL address of the request.

After we have written our test method, the source code of our test class looks as follows:

 1import com.github.tomakehurst.wiremock.junit.WireMockRule;
 2import org.junit.Before;
 3import org.junit.Rule;
 4import org.junit.Test;
 5import org.junit.experimental.categories.Category;
 6import org.junit.runner.RunWith;
 7import org.springframework.beans.factory.annotation.Autowired;
 8import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 9import org.springframework.boot.test.context.SpringBootTest;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12import org.springframework.test.web.servlet.MockMvc;
13import org.springframework.test.web.servlet.ResultActions;
14
15import static com.github.tomakehurst.wiremock.client.WireMock.*;
16import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
17import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
18
19@RunWith(SpringJUnit4ClassRunner.class)
20@SpringBootTest(classes = {ExampleApplication.class})
21@AutoConfigureMockMvc
22@Category(IntegrationTest.class)
23@ActiveProfiles(Profiles.INTEGRATION_TEST)
24public class FindVatPercentageMockTest {
25
26    private static final String COUNTRY_CODE = "FI";
27
28    @Rule
29    public WireMockRule wireMockRule = new WireMockRule(options()
30            .bindAddress("localhost")
31            .port(8080)
32    );
33
34    @Autowired
35    private MockMvc mockMvc;
36
37    @Before
38    public void configureDefaultResponse() {
39        givenThat(any(anyUrl())
40                .willReturn(ok())
41        );
42    }
43
44    @Test
45    public void shouldGetVatPercentageFromExternalApi() throws Exception {
46        findByCountryCode(COUNTRY_CODE);
47
48        verify(1, getRequestedFor(
49                urlEqualTo("/api/external/vat-percentage?countryCode=FI")
50        ));
51    }
52
53    private ResultActions findByCountryCode(String countryCode) throws Exception {
54        return mockMvc.perform(get("/api/vat-percentage")
55                .param("countryCode", countryCode)
56        );
57    }
58}

Again, even though this works, the expected URL address can become quite long if our request has multiple request parameters. That’s why it’s a good idea to make the following changes to our test method:

  1. Ignore the request parameter when we configure the expected URL address.
  2. Verify that the value of the countryCode request parameter is equal to: ‘FI’.

After we have made these changes, the source code of our test class looks as follows:

 1import com.github.tomakehurst.wiremock.junit.WireMockRule;
 2import org.junit.Before;
 3import org.junit.Rule;
 4import org.junit.Test;
 5import org.junit.experimental.categories.Category;
 6import org.junit.runner.RunWith;
 7import org.springframework.beans.factory.annotation.Autowired;
 8import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 9import org.springframework.boot.test.context.SpringBootTest;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12import org.springframework.test.web.servlet.MockMvc;
13import org.springframework.test.web.servlet.ResultActions;
14
15import static com.github.tomakehurst.wiremock.client.WireMock.*;
16import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
17import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
18
19@RunWith(SpringJUnit4ClassRunner.class)
20@SpringBootTest(classes = {ExampleApplication.class})
21@AutoConfigureMockMvc
22@Category(IntegrationTest.class)
23@ActiveProfiles(Profiles.INTEGRATION_TEST)
24public class FindVatPercentageMockTest {
25
26    private static final String COUNTRY_CODE = "FI";
27
28    @Rule
29    public WireMockRule wireMockRule = new WireMockRule(options()
30            .bindAddress("localhost")
31            .port(8080)
32    );
33
34    @Autowired
35    private MockMvc mockMvc;
36
37    @Before
38    public void configureDefaultResponse() {
39        givenThat(any(anyUrl())
40                .willReturn(ok())
41        );
42    }
43
44    @Test
45    public void shouldGetVatPercentageFromExternalApi() throws Exception {
46        findByCountryCode(COUNTRY_CODE);
47
48        verify(1, getRequestedFor(
49                urlMatching("/api/external/vat-percentage.+"))
50                .withQueryParam("countryCode", equalTo(COUNTRY_CODE))
51        );
52    }
53
54    private ResultActions findByCountryCode(String countryCode) throws Exception {
55        return mockMvc.perform(get("/api/vat-percentage")
56                .param("countryCode", countryCode)
57        );
58    }
59}

WireMock has a quite versatile support for mocking. Unfortunately we can only scratch the surface of that support because we don’t have time to cover all scenarios. That is why I recommend that you should take a look at the following documentation:

Additional Reading:

We have now learned how we can verify the received HTTP requests with WireMock. Next, we will find out when we should use WireMock.

When Should We Use WireMock?

The only thing that determines if we should use WireMock is the complexity of the code that uses the external REST API. I know that this isn’t a very helpful advice and that’s why I have written two rules that helps us to decide when we should use WireMock. These rules are:

  • If the code simply sends an HTTP request to the external REST API and returns the response as is, I think that we shouldn’t use WireMock because we don’t really miss any code if use other options such as Mockito.
  • If the code sends an HTTP request to the external REST API and manipulates the returned response or combines data from multiple sources, we should use WireMock because our code is so complex that it should be tested and WireMock allows us to write tests for this code.

I know that these rules are a bit extreme and most of time our code is placed in between of these two rules. In other words, I guess you just have to use the simplest tool that allows you to write automated tests for code that’s worth testing.

Let’s summarize what we learned from this webinar.

Summary

This webinar has taught us three things:

  • We can configure WireMock by using a custom JUnit 4 rule called: WireMockRule.
  • If the code simply sends an HTTP request to the external REST API and returns the response as is, we shouldn’t use WireMock.
  • If the code sends an HTTP request to the external REST API and manipulates the returned response or combines data from multiple sources, we should use WireMock.

Get the source code from Github