In this tutorial we will learn how we can add unit testing to our basic Spring Boot application that we have discussed in this tutorial Using SpringBoot with JPA tutorial
The starting point of this tutorial will be the basic template of a Web application which includes the web, data-jpa, h2 and thymeleaf dependencies:
$ spring init -dweb,data-jpa,h2,thymeleaf samplewebapp
Our project structure looks like this:
~/springboot:$ tree samplewebapp/
samplewebapp/
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── samplewebapp
│ │ └── DemoApplication.java
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
└── test
└── java
└── com
└── example
└── samplewebapp
└── DemoApplicationTests.java
In this project there is already a Tester class with a minimal Test included:
package com.example.samplewebapp;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Test
public void contextLoads() {}
}
The above is a sample default test class. Let’s examine it:
@RunWith(SpringRunner.class) : this annotation is a custom extension of JUnit library and it will invoke the class it’s referencing (SpringRunner.class )
The @SpringBootTest annotation tells Spring Boot to go and look for a main configuration class (one with @SpringBootApplication for instance), and use that to start a Spring application contex
@Test: This is a standard JUnit test annotation that will execute the method when the tests start. You can have one or more methods. If you have more than one method with that annotation it is not guaranteed that they will be executed in that order. For that you need to add the @FixMethodOrder(MethodSorters.NAME_ASCENDING) annotation to the class.
Also, one thing to note is that the Spring initializr already provided you the library needed to run the tests in your project:
<?xml version="1.0" encoding="UTF-8"?><project>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</project>
You can run this test in your IDE or on the command line (mvn test or gradle test) and it should pass.
Testing with MockMVC
There are mainly two strategies for testing Spring Boot application: you can write Controller tests using the MockMVC approach, or using the RestTemplate. The first strategy (MockMVC) will be shown here and it should be your option if you want to code a real Unit Test. On the other hand, the RestTemplate strategy should be used if you intend to write an Integration Test.
So, here is our basic Controller:
package com.example.samplewebapp;
import java.net.URI;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
@RestController
public class PersonController {
@Autowired private PersonRepository repository;
@GetMapping("/all")
public Iterable<Person> retrieveAllPersons() {
return repository.findAll();
}
@GetMapping("/person/{id}")
public Person retrieveStudent(@PathVariable long id) {
Optional<Person> person = repository.findById(id);
if (!person.isPresent()) throw new RuntimeException("id-" + id);
return person.get();
}
@PostMapping("/add")
public ResponseEntity<Object> createStudent(@RequestBody Person person) {
person = repository.save(person);
URI location =
ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(person.getId())
.toUri();
return ResponseEntity.created(location).build();
}
}
We will now write a minimal Test class using MockMVC:
package com.example.samplewebapp;
import static org.hamcrest.CoreMatchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import com.fasterxml.jackson.databind.ObjectMapper;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class DemoApplicationTests {
@Autowired private MockMvc mvc;
@Test
public void checkAll() throws Exception {
mvc.perform(get("/all").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].name", is("Jack")));
}
@Test
public void findById() throws Exception {
mvc.perform(get("/person/{id}", 1))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.id", is(1)))
.andExpect(jsonPath("$.name", is("Jack")));
}
@Test
public void add() throws Exception {
Person user = new Person("Mark", "Stark");
mvc.perform(post("/add").contentType(MediaType.APPLICATION_JSON).content(asJsonString(user)))
.andExpect(status().isCreated());
}
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
In this example, we are using the annotation @AutoConfigureMockMvc. This annotation tells SpringRunner to configure the MockMvc instance that will be used to make our REST calls. MockMvc allows us to test our @RestController class without the cost of starting a Web server.
Then we use the method perform of the class MockMVC to execute the actual REST calls. You can run the above test from within your IDE Run As > JUnitTest or with ‘spring test’ from the Command Line:
