One of the ways to write robust code is to write unit test cases.
This way you make sure that the changes you make on a code doesn’t break any existing functionality.
This helps in preventing bugs later.
Spring Boot provides an easier way to write unit test cases for your REST APIs.
Let’s see how to do it.
Unit Test Case for testing a JSON response:
First let’s create a sample REST API using @RestController annotation:
@RestController
public class TestController {
@GetMapping("/books/1")
public Book getBook() {
return new Book(1, "The Great Gatsby", "F. Scott Fitzgerald");
}
@GetMapping("/books")
public List<Book> getBooks() {
List<Book> books = Arrays.asList(
new Book(1, "The Great Gatsby", "F. Scott Fitzgerald"),
new Book(2, "1984", "George Orwell"),
new Book(3, "To Kill a Mockingbird", "Harper Lee")
);
return books;
}
}
There are two APIs in the above controller class.
One “/books/1” which returns a single book.
Another “/books” which returns all the books.
Here is the book model class:
public class Book {
private int id;
private String title;
private String author;
// Constructors, getters, and setters
public Book(int id, String title, String author) {
this.id = id;
this.title = title;
this.author = author;
}
// Getters and setters
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
Now if you run the application and hit those APIs you will get the book details back.
How do we test those APIs without running the application ?
Spring provides “MockMvc” class for the same.
This comes as part of the spring-book-starter-test dependency.
This dependency is automatically added to your pom.xml if you generate the spring boot app through spring initializr. If not make sure you add the dependency.
Here is a sample test case:
@SpringBootTest
@AutoConfigureMockMvc
public class ControllerTests {
@Autowired
private MockMvc mockMVC;
@Test
void testGreetingAPI() throws Exception {
String result = "{'id':1,'title':'The Great Gatsby','author':'F. Scott Fitzgerald'}";
this.mockMVC.perform(get("/books/1"))
.andExpect(status().isOk())
.andExpect(content().json(result));
}
}
Notice the two annotations:
@SpringBootTest
@AutoConfigureMockMvc
You need these two to let know Spring that this is a unit test case and that it needs to configure MockMvc object to perform mock REST API calls.
We use mockMvc object thus provided to make a mockAPI call in the above test case.
The “GET” REST API is hit through get() method passed as a parameter to perform() method of MockMvc object.
The status returned is checked if it is HttpStatus.OK
the content returned is checked using content() and json() methods.
Printing a Test Case Response:
If you want to print the test case result you can add andDo() method in the above test case like below:
@Test
void testGreetingAPI() throws Exception {
String result = "{'id':1,'title':'The Great Gatsby','author':'F. Scott Fitzgerald'}";
this.mockMVC.perform(get("/books/1"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().json(result));
}
Here is a sample output printed on adding “andDo” method:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /books
Parameters = {}
Headers = []
Body = null
Session Attrs = {}
Handler:
Type = com.example.demo.TestController
Method = com.example.demo.TestController#getBooks()
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = [{"id":1,"title":"The Great Gatsby","author":"F. Scott Fitzgerald"},{"id":2,"title":"1984","author":"George Orwell"},{"id":3,"title":"To Kill a Mockingbird","author":"Harper Lee"}]
Forwarded URL = null
Redirected URL = null
Cookies = []
Checking a specific field in JSON Response:
What if you want to check a particular field in the JSON response?
You can do that using jsonPath() method as below:
.andExpect(jsonPath("$.[1].title").value("1984"));
Here $ represents the root JSON element.
To find out a particular element in the list you pass the object index in square brackets as above.
If the returned JSON response is not a list then you can leave out the above square bracket.
Here is the entire test case:
@Test
void testGreetingAPITitle() throws Exception {
String result = "1984";
this.mockMVC.perform(get("/books"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.[1].title").value(result));
}
Testing for an Exception :
Now let’s write a unit test case for an exception scenario.
Let’s say we are hitting a wrong URL , it should throw 404 NOT FOUND status.
We can test it this way:
@Test
void testGreetingAPI_NOTFOUND() throws Exception {
String result = "{'id':1,'title':'The Great Gatsby','author':'F. Scott Fitzgerald'}";
this.mockMVC.perform(get("/newbooks/1"))
.andDo(print())
.andExpect(status().isNotFound());
}
I have updated the API to /newbooks/1 instead of /books/1.
On running the test case it returns true.
The line andExpect(status().isNotFound()) checks for the 404 HTTP STATUS and returns true if it finds one.
That’s it!
Leave a Reply