crop man holding hammer in workshop

How to write unit test cases for REST APIs in Spring Boot?

crop man holding hammer in workshop
Photo by Ksenia Chernaya on Pexels.com

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!


Posted

in

by

Comments

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading