0

I'm writing unit test for controller, this time I purposely made a request which will trigger ConstrainViolationException, in mockmvc instead of expect it throws the error. How can I tell spring not to throw it and let mvcresult verify the result

Here is my code snippet

@WebMvcTest(ProductController.class)
public class ProductControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private ProductService productService;

    @BeforeEach
    public void setUp() throws Exception
    {
        this.mockMvc = MockMvcBuilders.standaloneSetup(new ProductController(productService))
                .setControllerAdvice(new ExceptionAdvice())
                .build();
    }

    @Test
    public void createProduct_With_InvalidRequest_ShouldReturn_BadRequest() throws Exception {

        ProductWebRequest productWebRequest = ProductWebRequest.builder()
                .sku(null)
                .productName(null)
                .barcode(null)
                .build();

        ProductServiceWebRequest productServiceWebRequest = ProductServiceWebRequest.builder()
                .sku(productWebRequest.getSku())
                .productName(productWebRequest.getProductName())
                .barcode(productWebRequest.getBarcode())
                .build();

        ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
        String json = ow.writeValueAsString(productWebRequest);

        BDDMockito.given(productService.createProduct(productServiceWebRequest)).willThrow(ConstraintViolationException.class);

        mockMvc.perform(MockMvcRequestBuilders.post("/v1/products")
                .contentType(MediaType.APPLICATION_JSON).content(json))
                .andExpect(MockMvcResultMatchers.status().isBadRequest())
                .andExpect(mvcResult -> Assertions.assertTrue(mvcResult.getResolvedException() instanceof ConstraintViolationException));
    }
}

ProductController.java

@RestController
@RequestMapping("/v1")
public class ProductController {

    private final ProductService productService;

    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @PostMapping(value = "/products", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public WebResponse createProduct(@RequestBody ProductWebRequest request)
    {
        ProductServiceWebRequest productServiceWebRequest = ProductServiceWebRequest.builder()
                .sku(request.getSku())
                .productName(request.getProductName())
                .barcode(request.getBarcode())
                .description(request.getDescription())
                .brand(request.getBrand())
                .material(request.getMaterial())
                .motif(request.getMotif())
                .colour(request.getColour())
                .price(request.getPrice())
                .weight(request.getWeight())
                .length(request.getLength())
                .width(request.getWidth())
                .height(request.getHeight())
                .category(request.getCategory())
                .hasVariant(request.getHasVariant())
                .build();

        ProductCreatedWebResponse productCreatedWebResponse = productService.createProduct(productServiceWebRequest);

        return WebResponse.builder()
                .statusCode(HttpStatus.OK.value())
                .statusName(HttpStatus.OK.name())
                .data(productCreatedWebResponse)
                .build();
    }

ExceptionAdvice.java

@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
@Slf4j
public class ExceptionAdvice extends ResponseEntityExceptionHandler {

    @Override
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request)
    {
        List<String> errors = new ArrayList<>();

        for (FieldError error : ex.getBindingResult().getFieldErrors())
        {
            errors.add(error.getField() + " : " + error.getDefaultMessage());
        }
        for (ObjectError error : ex.getBindingResult().getGlobalErrors())
        {
            errors.add(error.getObjectName() +" : "+ error.getDefaultMessage());
        }

        WebResponse webResponse = WebResponse.builder()
                .error("API_ERROR")
                .errorDetail("REQUEST_NOT_CONFORM_PAYLOAD_STANDARD")
                .statusCode(HttpStatus.BAD_REQUEST.value())
                .statusName(HttpStatus.BAD_REQUEST.name())
                .build();

        log.error(errors.toString());

        return handleExceptionInternal(ex, webResponse, headers, HttpStatus.BAD_REQUEST, request);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({ConstraintViolationException.class})
    public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex, WebRequest request)
    {
        List<String> errors = new ArrayList<String>();

        for (ConstraintViolation<?> violation : ex.getConstraintViolations())
        {
            errors.add(violation.getRootBeanClass().getSimpleName() + " - " + violation.getPropertyPath() + " : " + violation.getMessage().toUpperCase());
        }

        WebResponse webResponse = WebResponse.builder()
                .error("API_FAIL")
                .errorDetail("REQUEST_NOT_CONFORM_PAYLOAD_STANDARD")
                .statusCode(HttpStatus.BAD_REQUEST.value())
                .statusName(HttpStatus.BAD_REQUEST.name())
                .build();

        log.error(errors.toString());

        return new ResponseEntity<Object>(webResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST);
    }
}

And this is the error that appears

(ProductControllerTest.java:50)
Caused by: javax.validation.ConstraintViolationException

I know this will throw an exception, but how can I use that exception in the expect section for assertions?

Ravi Mukti
  • 11
  • 5

1 Answers1

0

ConstraintValidationException is a bit of special case.

To ensure that its handled properly you can do like this ...

@Before
    public void setup() throws Exception {
        this.mockMvc = standaloneSetup(new MyController())
                .setControllerAdvice(new CustomExceptionHandler(), new ConstraintViolationsHandler())
                .build();
    }

Does this solve your problem ? Tell me in the comments.

Arthur Klezovich
  • 2,595
  • 1
  • 13
  • 17
  • i've been added some code as you say `@BeforeEachpublicvoidsetUp()throwsException{this.mockMvc=MockMvcBuilders.standaloneSetup(newProductController(productService)).setControllerAdvice(newExceptionAdvice()).build();}` i dont know is that the right way as you say? cause i still get the same result `Caused by: javax.validation.ConstraintViolationException` – Ravi Mukti Nov 26 '21 at 09:22
  • https://stackoverflow.com/questions/45070642/springboot-doesnt-handle-org-hibernate-exception-constraintviolationexception Try like this – Arthur Klezovich Nov 26 '21 at 09:25
  • OK, I'll try it first, I'll let you know later – Ravi Mukti Nov 26 '21 at 09:31
  • I've checked the solution you gave, but it doesn't seem like what I experienced, because I was able to give a response of 400 as shown below [response](https://imgur.com/a/oMBUNl5), but what I'm confused about is why when the mvc.perform method expect never executes, it keeps throwing exceptions, I know it will throw an exception, because it is as expected – Ravi Mukti Nov 26 '21 at 09:43