简体   繁体   中英

How to test InternalServerError using mockito in Repository?

I am writing a test to test the POST method for failure case in the controller. It returns a 415, I am expecting 500. I have mocked the response using mockito. ControllerTest

@Test
@DisplayName("POST /customers - Failure")
void createProductShouldFail() throws Exception {
    // Setup mocked service
    when(customerService.save(any(Customer.class))).thenThrow(HttpServerErrorException.InternalServerError.class);
    RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/customers").accept(MediaType.APPLICATION_JSON)
            .content("{\"name\":\"John\"}");
    MvcResult result=mockMvc.perform(requestBuilder).andReturn();
    MockHttpServletResponse response = result.getResponse();
            // Validate the response code and content type
    assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
}

Controller

    @PostMapping(path = "/customers")
public ResponseEntity<Customer> saveCustomer(@RequestBody Customer customer){
    try {
        // Create the new product
        Customer savedCustomer = customerService.save(customer);
        // Build a created response
        return ResponseEntity
                .created(new URI("/customers/" + savedCustomer.getId()))
                .body(savedCustomer);
    } catch (URISyntaxException e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
}

Error:

      HTTP Method = POST
      Request URI = /customers
       Parameters = {}
          Headers = [Accept:"application/json", Content-Length:"15"]
             Body = {"name":"John"}
    Session Attrs = {}

Handler:
             Type = com.prabhakar.customer.controller.CustomerController
           Method = com.prabhakar.customer.controller.CustomerController#saveCustomer(Customer)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = org.springframework.web.HttpMediaTypeNotSupportedException

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 415
    Error message = null
          Headers = [Accept:"application/json, application/*+json"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

org.opentest4j.AssertionFailedError: 
Expected :500
Actual   :415

But 415-Unsupported Media Type client error response code.

I have used the same payload for this method,it works.

  

      @Test
        @DisplayName("POST /customers - Success")
        void createProductShouldSucceed() throws Exception {
            // Setup mocked service
            Customer mockCustomer = new Customer(1L, "John");
            when(customerService.save(any(Customer.class))).thenReturn(mockCustomer);
            this.mockMvc.perform(post("/customers")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content("{\"name\":\"John\"}"))
                    // Validate the response code and content type
                    .andExpect(status().isCreated())
                    .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                    //Validate returned json fields
                    .andExpect(jsonPath("$.id").value(1L))
                    .andExpect(jsonPath("$.name").value("John"));
        }

Update I have added
@RestController
@EnableWebMvc

this throws an error as mocked But the code breaks near mockmvc.perform .

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpServerErrorException$InternalServerError

How can I verify if this is working. assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());

You can reference Spring MVC Test Framework - Unsupported Media Type

You may be missing @EnableWebMvc annotation in your controller.

EDIT - for Comment:

Instead of

RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/customers").accept(MediaType.APPLICATION_JSON)
        .content("{\"name\":\"John\"}");
MockHttpServletResponse response = result.getResponse();
        // Validate the response code and content type
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), 
response.getStatus());
    

Try:

  mockMvc.perform(requestBuilder)
            .andExpect(status().isInternalServerError());

There are two thing you must have in account to solve the problem:

  • First , Instead of use .accept(MediaType.APPLICATION_JSON) you must use .contentType(MediaType.APPLICATION_JSON) .

  • Second , the other thing you must have in account is, if you are not handling the exception (using a controller advice or other way) you must do it, because when you execute the firts step you will receive the following error:

    • org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpServerErrorException$InternalServerError

The workaround that I took was use @ExceptionHandler in the CustomerController to test your code (this isn't the best place to do this, depending what you are doing. Instead use a @ControllerAdvice. You can find some examples here https://www.baeldung.com/exception-handling-for-rest-with-spring ).

Below the complete code that are used to recreate your case.

Customer.class

public class Customer {
    private Long id;
    private String name;

    public Customer(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    // + getter and setter
}

CustomerController.class

@RestController
public class CustomerController {

    private final CustomerService customerService;

    public CustomerController(CustomerService customerService) {
        this.customerService = customerService;
    }

    @PostMapping(path = "/customers")
    public ResponseEntity<Customer> saveCustomer(@RequestBody Customer customer) {
        try {
            // Create the new product
            Customer savedCustomer = customerService.save(customer);
            // Build a created response
            return ResponseEntity
                    .created(new URI("/customers/" + savedCustomer.getId()))
                    .body(savedCustomer);
        } catch (URISyntaxException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    // Code used to avoid the error explained in the second step
    @ExceptionHandler
    public ResponseEntity<?> handlingInternalServerError(HttpServerErrorException.InternalServerError ex) {
        // code to be executed when the exception is thrown (logs, ...)
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

CustomerService.class

@Service
public class CustomerService {

    public Customer save(Customer customer) {
        return customer;
    }
}

CustomerControllerTest.class

@SpringBootTest
@AutoConfigureMockMvc
class CustomerControllerTest {

    @MockBean
    private CustomerService customerService;

    @Autowired
    private MockMvc mockMvc;

    @Test
    @DisplayName("POST /customers - Failure")
    void saveCustomer() throws Exception {

        Customer customerMock = new Customer(1L, "John");
        // Setup mocked service
        when(customerService.save(any(Customer.class))).thenThrow(HttpServerErrorException.InternalServerError.class);
        RequestBuilder requestBuilder = post("/customers")
                .content("{\"name\":\"John\"}")
                .accept(MediaType.APPLICATION_JSON)
                .contentType(MediaType.APPLICATION_JSON);
        MvcResult result = mockMvc.perform(requestBuilder).andReturn();

        MockHttpServletResponse response = result.getResponse();

        // Validate the response code and content type
        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus());
    }
}

NOTE: This test was executed using Java 8 and JUnit5

Other NOTE based on your comment: Ok. @prabhakar-maity, my recommendation based in your case is to use a @ExceptionHandler or @ControllerAdvice instead of try...catch. For example, you have 6 methods in your controller or several controllers and want to handle the same exception (Internal Server Error) and return the same info, so you'll have to implement a try..catch in each method, while using @ControllerAdive (multiple controllers) or @ExceptionHandler (one controller) you implement your logic in one place

Check this question for more info LINK

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM