Using Spring Cloud Contract With JUnit 5
After we have finished this webinar, we:
- Can create a Maven project that uses Spring Cloud Contract and JUnit 5.
- Know how we can use Spring Cloud Contract and JUnit 5 when we implement REST producer and consumer services.
This webinar assumes that:
- You are familiar with JUnit 5
- You can implement a REST producer service
- You can implement a REST consumer service
Before we start, I want to point out that we must use the Spring Cloud Contract version 2.1.0.M2 (milestone 2) because the stable Spring Cloud Contract version has no support for JUnit 5.
Get the source code from Github
Modifying the REST Producer Service
When we want to use JUnit 5 in our REST producer service project, we have to modify our POM file and the base class that is extended by the actual test classes generated by the Spring Cloud Contract Maven plugin.
Let’s start by modifying our POM file.
Modifying Our POM File
We can make the required changes to our POM file by following these steps:
First, because we have to use the Spring Cloud Contract version 2.1.0.M2, we have to ensure that Maven uses the Spring Milestone repositories when it downloads the dependencies of our REST producer service and the Maven plugins used by our Maven build.
We can do this by adding the following snippet to our pom.xml file:
1<repositories>
2 <repository>
3 <id>repository.spring.milestone</id>
4 <name>Spring Milestone Repository</name>
5 <url>http://repo.spring.io/milestone</url>
6 </repository>
7</repositories>
8
9<pluginRepositories>
10 <pluginRepository>
11 <id>repository.spring.milestone</id>
12 <name>Spring Milestone Repository</name>
13 <url>http://repo.spring.io/milestone</url>
14 </pluginRepository>
15</pluginRepositories>
Second, we have to change the version of the Spring Cloud Contract BOM that is used by our Maven project. This ensures that our Maven project uses the correct dependency versions.
We can make the required change by setting the value of the version element to 2.1.0.M2. After we have made this change, the relevant part of our POM file looks as follows:
1<dependencyManagement>
2 <dependencies>
3 <dependency>
4 <groupId>org.springframework.cloud</groupId>
5 <artifactId>spring-cloud-contract-dependencies</artifactId>
6 <version>2.1.0.M2</version>
7 <type>pom</type>
8 <scope>import</scope>
9 </dependency>
10 </dependencies>
11</dependencyManagement>
Third, we have to declare the JUnit 5 dependencies in our POM file. We can do this by adding the junit-jupiter-api and junit-jupiter-engine dependencies to the dependencies section of our pom.xml file.
In other words, we have to add the following snippet to the dependencies section of our POM file:
1<dependency>
2 <groupId>org.junit.jupiter</groupId>
3 <artifactId>junit-jupiter-api</artifactId>
4 <version>5.3.2</version>
5 <scope>test</scope>
6</dependency>
7<dependency>
8 <groupId>org.junit.jupiter</groupId>
9 <artifactId>junit-jupiter-engine</artifactId>
10 <version>5.3.2</version>
11 <scope>test</scope>
12</dependency>
Fourth, because we use JUnit 5, we have to ensure that Maven runs our unit tests by using the Maven Surefire plugin (version 2.22.1). This version provides a built-in support for running JUnit 5 tests. In other words, we don’t have to configure the junit-platform-surefire-provider anymore.
We can configure the version of the Maven Surefire plugin by adding the following snippet to the plugins section of our POM file:
1<plugin>
2 <groupId>org.apache.maven.plugins</groupId>
3 <artifactId>maven-surefire-plugin</artifactId>
4 <version>2.22.1</version>
5</plugin>
Fifth, we have to make the following changes to the configuration of the Spring Cloud Contract Maven plugin:
- Set the version of the Spring Cloud Contract Maven plugin to 2.1.0.M2.
- Ensure that the Spring Cloud Contract Maven plugin generates test classes which use JUnit 5.
After we have the required changes, the configuration of the Spring Cloud Contract Maven plugin looks as follows:
1<plugin>
2 <groupId>org.springframework.cloud</groupId>
3 <artifactId>spring-cloud-contract-maven-plugin</artifactId>
4 <version>2.1.0.M2</version>
5 <extensions>true</extensions>
6 <configuration>
7 <baseClassForTests>com.testwithspring.master.webinar.BaseTest</baseClassForTests>
8 <testFramework>JUNIT5</testFramework>
9 </configuration>
10</plugin>
We have now made the required changes to our Maven build. Next, we will modify the base class of our unit tests.
Modifying Our Base Test Class
As we remember
, the base class of our unit tests configures the system under test by using a setup method that is run before a test method is run. That’s why the only change that we must do is to replace the @Before annotation with the new @BeforeEach annotation.
After we have made this change, the source code of the BaseTest class looks as follows:
1import io.restassured.module.mockmvc.RestAssuredMockMvc;
2import org.junit.jupiter.api.BeforeEach;
3
4import static org.mockito.BDDMockito.given;
5import static org.mockito.Mockito.mock;
6
7public abstract class BaseTest {
8
9 @BeforeEach
10 public void setup() {
11 TaskService service = mock(TaskService.class);
12
13 RestAssuredMockMvc.standaloneSetup(new TaskController(service));
14 given(service.findById(1L))
15 .willReturn(new Task(1L, "Write our first contract"));
16 }
17}
We have now made the required changes to our REST producer service. Let’s move and find out how we can make the required changes to our REST consumer service.
Modifying the REST Consumer Service
When we want to use JUnit 5 in our REST consumer project, we have to modify our POM file and the test classes which ensure that the system under test is working as expected when the contract between the consumer service and the producer service hasn’t changed.
Let’s start by modifying our POM file.
Modifying Our Pom File
We can make the required changes to our POM file by following these steps:
First, because we have to use the Spring Cloud Contract version 2.1.0.M2, we have to ensure that Maven uses the Spring Milestone repository when it downloads the dependencies of our REST consumer service.
We can do this by adding the following snippet to our pom.xml file:
1<repositories>
2 <repository>
3 <id>repository.spring.milestone</id>
4 <name>Spring Milestone Repository</name>
5 <url>http://repo.spring.io/milestone</url>
6 </repository>
7</repositories>
Second, we have to change the version of the Spring Cloud Contract BOM that is used by our Maven project. This ensures that our Maven project uses the correct dependency versions.
We can make the required change by setting the value of the version element to 2.1.0.M2. After we have made this change, the relevant part of our POM file looks as follows:
1<dependencyManagement>
2 <dependencies>
3 <dependency>
4 <groupId>org.springframework.cloud</groupId>
5 <artifactId>spring-cloud-contract-dependencies</artifactId>
6 <version>2.1.0.M2</version>
7 <type>pom</type>
8 <scope>import</scope>
9 </dependency>
10 </dependencies>
11</dependencyManagement>
Third, we have to declare the JUnit 5 dependencies in our POM file. We can do this by adding the junit-jupiter-api and junit-jupiter-engine dependencies to the dependencies section of our pom.xml file.
In other words, we have to add the following snippet to the dependencies section of our POM file:
1<dependency>
2 <groupId>org.junit.jupiter</groupId>
3 <artifactId>junit-jupiter-api</artifactId>
4 <version>5.3.2</version>
5 <scope>test</scope>
6</dependency>
7<dependency>
8 <groupId>org.junit.jupiter</groupId>
9 <artifactId>junit-jupiter-engine</artifactId>
10 <version>5.3.2</version>
11 <scope>test</scope>
12</dependency>
Fourth, because we use JUnit 5, we have to ensure that Maven runs our unit tests by using the Maven Surefire plugin (version 2.22.1). This version provides a built-in support for running JUnit 5 tests. In other words, we don’t have to configure the junit-platform-surefire-provider anymore.
We can configure the version of the Maven Surefire plugin by adding the following snippet to the plugins section of our POM file:
1<plugin>
2 <groupId>org.apache.maven.plugins</groupId>
3 <artifactId>maven-surefire-plugin</artifactId>
4 <version>2.22.1</version>
5</plugin>
We have now made the required changes to our Maven project. Next, we will find out how we can modify our test classes.
Modifying Our Test Classes
As we remember , our REST consumer service project has two tests. These tests help us to ensure that:
- The contract provided by our REST provider service hasn’t changed.
- Our REST consumer service is working as expected when the requested task is found.
We can migrate these tests from JUnit 4 to JUnit 5 by following these steps:
First, we have to make the following changes to the TaskContractTest class:
- Use the
SpringExtensionclass instead of theSpringRunnerclass. - Replace the
@Beforeannotation with the@BeforeEachannotation. - Replace the
org.junit.Testannotation with theorg.junit.jupiter.api.Testannotation. - Configure the display names of our test methods by using the
@DisplayNameannotation.
After we have made these changes, the source code of the TaskContractTest class looks as follows:
1import org.junit.jupiter.api.BeforeEach;
2import org.junit.jupiter.api.DisplayName;
3import org.junit.jupiter.api.Test;
4import org.junit.jupiter.api.extension.ExtendWith;
5import org.springframework.boot.test.context.SpringBootTest;
6import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
7import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
8import org.springframework.http.HttpStatus;
9import org.springframework.http.ResponseEntity;
10import org.springframework.test.context.junit.jupiter.SpringExtension;
11import org.springframework.web.client.RestTemplate;
12
13import static org.assertj.core.api.Assertions.assertThat;
14
15@ExtendWith(SpringExtension.class)
16@SpringBootTest
17@AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.LOCAL,
18 ids = "com.testwithspring.master.webinar:contract-rest-service-junit5:0.0.1-SNAPSHOT:stubs:9090"
19)
20public class TaskContractTest {
21
22 private RestTemplate restTemplate;
23
24 @BeforeEach
25 public void setup() {
26 restTemplate = new RestTemplate();
27 }
28
29 @Test
30 @DisplayName("Should return the HTTP status code 200")
31 public void shouldReturnHttpStatusCodeOk() {
32 ResponseEntity<Task> taskResponse = restTemplate.getForEntity(
33 "http://localhost:9090/api/task/1",
34 Task.class
35 );
36 assertThat(taskResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
37 }
38
39 @Test
40 @DisplayName("Should return a task that has the correct id")
41 public void shouldReturnTaskWithCorrectId() {
42 ResponseEntity<Task> taskResponse = restTemplate.getForEntity(
43 "http://localhost:9090/api/task/1",
44 Task.class
45 );
46
47 Task task = taskResponse.getBody();
48 assertThat(task.getId()).isEqualByComparingTo(1L);
49 }
50
51 @Test
52 @DisplayName("Should return a task that has the correct title")
53 public void shouldReturnTaskWithCorrectTitle() {
54 ResponseEntity<Task> taskResponse = restTemplate.getForEntity(
55 "http://localhost:9090/api/task/1",
56 Task.class
57 );
58
59 Task task = taskResponse.getBody();
60 assertThat(task.getTitle()).isEqualTo("Write our first contract");
61 }
62}
Second, we have to make the following changes to the FindTaskByIdWhenTaskIsFoundTest class:
- Use the
SpringExtensionclass instead of theSpringRunnerclass. - Replace the
org.junit.Testannotation with theorg.junit.jupiter.api.Testannotation. - Configure the display names of our test methods by using the
@DisplayNameannotation.
After we have made these changes, the source code of our test class looks as follows:
1import org.junit.jupiter.api.DisplayName;
2import org.junit.jupiter.api.Test;
3import org.junit.jupiter.api.extension.ExtendWith;
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.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
8import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
9import org.springframework.http.MediaType;
10import org.springframework.test.context.junit.jupiter.SpringExtension;
11import org.springframework.test.web.servlet.MockMvc;
12
13import static org.assertj.core.api.Assertions.assertThat;
14import static org.hamcrest.Matchers.is;
15import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
16import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
17import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
18import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
19
20@ExtendWith(SpringExtension.class)
21@SpringBootTest
22@AutoConfigureMockMvc
23@AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.LOCAL,
24 ids = "com.testwithspring.master.webinar:contract-rest-service-junit5:0.0.1-SNAPSHOT:stubs:8080"
25)
26public class FindTaskByIdWhenTaskIsFoundTest {
27
28 @Autowired
29 private MockMvc mockMvc;
30
31 @Test
32 @DisplayName("Should return the HTTP status code OK")
33 public void shouldReturnHttpStatusCodeOk() throws Exception {
34 mockMvc.perform(get("/api/task/{id}", 1L))
35 .andExpect(status().isOk());
36 }
37
38 @Test
39 @DisplayName("Should return the found task as JSON")
40 public void shouldReturnFoundTaskAsJson() throws Exception {
41 mockMvc.perform(get("/api/task/{id}", 1L))
42 .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8));
43 }
44
45 @Test
46 @DisplayName("Should return the information of the correct task")
47 public void shouldReturnInformationOfCorrectTask() throws Exception {
48 mockMvc.perform(get("/api/task/{id}", 1L))
49 .andExpect(jsonPath("$.id", is(1)))
50 .andExpect(jsonPath("$.title", is("Write our first contract")));
51 }
52}
We have now made the required changes to our REST consumer service project. Let’s summarize what we learned from this webinar.
Summary
This webinar has us four things:
- We have to use the Spring Cloud Contract version 2.1.0.M2 because the stable version doesn’t support JUnit 5.
- If we want to use JUnit 5 when we write our REST producer service, we have to change the configuration of the Spring Cloud Contract Maven plugin. To be more specific, we have to set the value of the
testFrameworkconfiguration option toJUNIT5. - We have to run our tests by using the Maven Surefire plugin 2.22.1 because it has a built-in support for running tests that use JUnit 5.
- We can migrate our test classes from JUnit 4 to JUnit 5 by replacing the JUnit 4 specific annotations with the new annotations provided by JUnit 5.