Search code examples
spring-bootmockitojunit5

Junit test cases for Rest API using Junit and Mockito


Junit test cases for API's

I'm new to Junit and Mockito, trying to write unit test cases for my controller class to test my APIs.

Here is the controller class

    package com.mylearnings.controller;
    
    import com.mylearnings.modal.Product;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    
    @RestController
    public class ProductController {
    
        private HashMap<String, Product> productCatalog = new HashMap<>();
    
        @PostMapping("/product")
        public ResponseEntity addProduct(@RequestBody Product product) {
            productCatalog.put(product.getId(), product);
            return new ResponseEntity("product added successfully", HttpStatus.CREATED);
        }
    
        @GetMapping("/product/{id}")
        public ResponseEntity getProductDetails(@PathVariable String id) {
            return ResponseEntity.ok(productCatalog.get(id));
        }
    
        @GetMapping("/product")
        public List<Product> getProductList() {
            return new ArrayList<>(productCatalog.values());
        }
    
        @PutMapping("/product")
        public String updateProduct(@RequestBody Product product) {
            productCatalog.put(product.getId(), product);
            return "product updated successfully";
        }
    
        @DeleteMapping("/product/{id}")
        public String deleteProduct(@PathVariable String id) {
            productCatalog.remove(id);
            return "product deleted successfully";
        }
    }

I have tried the following Added @ExtendWith(MockitoExtension.class) and tried but still it's failing

    package com.mylearnings.controller;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.mylearnings.modal.Product;
    import org.junit.jupiter.api.Test;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    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.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.MvcResult;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.web.context.WebApplicationContext;
    
    import java.util.Map;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.mockito.Mockito.when;
    
    @SpringBootTest
    @AutoConfigureMockMvc
    public class ProductControllerTest {
    
        @Autowired
        private MockMvc mockMvc;
    
        @Autowired
        private WebApplicationContext webApplicationContext;
    
        @Mock
        private Map<String, Product> productCatalog;
    
        @InjectMocks
        private ProductController productController;
    
        @Test
        public void testAddProduct() throws Exception {
            MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/product").contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(new Product("MS116", "Dell MS116", "Dell MS116 Usb wired optical mouse", "", 229d)))).andReturn();
    
            assertEquals(201, mvcResult.getResponse().getStatus());
        }
    
        @Test
        public void testGetProductDetails() throws Exception {
            Product product = new Product("MS116", "Dell MS116", "Dell MS116 Usb wired optical mouse", "", 229d);
            when(productCatalog.get("MS116")).thenReturn(product);
            MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/product/{id}", "MS116").accept(MediaType.APPLICATION_JSON)).andReturn();
    
            assertEquals(200, mvcResult.getResponse().getStatus());
            Product result = new ObjectMapper().readValue(mvcResult.getResponse().getContentAsString(), Product.class);
            assertEquals(product, result);
        }
    }

the test case testGetProductDetails() is failing, I'm not sure whether it is because of map?


Solution

  • In order to be able to inject mocks into Application context using (@Mock and @InjectMocks) and make it available for you MockMvc, you can try to init MockMvc in the standalone mode with the only ProductController instance enabled (the one that you have just mocked).

    Example:

    package com.mylearnings.controller;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.mockito.Mockito.when;
    
    import com.mylearnings.controller.Product;
    import com.mylearnings.controller.ProductController;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.util.HashMap;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.junit.jupiter.MockitoExtension;
    import org.springframework.http.MediaType;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.MvcResult;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    
    @ExtendWith(MockitoExtension.class)
    public class ProductControllerTest {
    
        private MockMvc mockMvc;
    
        @Mock
        private HashMap<String, Product> productCatalog;
    
        @InjectMocks
        private ProductController productController;
    
        @BeforeEach
        public void setup() {
            mockMvc = MockMvcBuilders.standaloneSetup(productController)
                    .build();
        }
    
        @Test
        public void testAddProduct() throws Exception {
            MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/product").contentType(MediaType.APPLICATION_JSON)
                            .content(new ObjectMapper().writeValueAsString(new Product("MS116", "Dell MS116", "Dell MS116 Usb wired optical mouse", "", 229d))))
                    .andReturn();
    
            assertEquals(201, mvcResult.getResponse().getStatus());
        }
    
        @Test
        public void testGetProductDetails() throws Exception {
            Product product = new Product("MS116", "Dell MS116", "Dell MS116 Usb wired optical mouse", "", 229d);
            when(productCatalog.get("MS116")).thenReturn(product);
            MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/product/{id}", "MS116").accept(MediaType.APPLICATION_JSON)).andReturn();
    
            assertEquals(200, mvcResult.getResponse().getStatus());
            Product result = new ObjectMapper().readValue(mvcResult.getResponse().getContentAsString(), Product.class);
            assertEquals(product, result);
        }
    
    }