Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

In the previous blog entry titled Turning Spring MVC exceptions into status codes with messages, I mapped exceptions into HTTP status codes.

Test Controller

In this blog we will test those and verify that everything is working correctly. I started with a test controller that I would use to generate the exceptions and the message. I created controller methods that would generate consistent results:

  • ResourceException with an HTTP 400 and a message
  • ResourceException with an HTTP 404 and a message
  • RuntimeException without any developer customization
  • Exception with a message

I wanted to ensure that I had consistent behavior and I was able to pull information from the exception that would be relevant to the client/consumer, but I also wanted to ensure that I never returned a stacktrace to the outside world.

package com.javaninja.cam.spring;


import com.javaninja.cam.dto.Prospect;
import com.javaninja.cam.exception.ResourceException;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {

    @RequestMapping(value = "/test/resourceException400")
    public Prospect getResourceException400() {
        throw new ResourceException(HttpStatus.BAD_REQUEST, "Test Bad Request Exception message");
    }

    @RequestMapping(value = "/test/resourceException404")
    public Prospect getResourceException404() {
        throw new ResourceException(HttpStatus.NOT_FOUND, "Test Not Found Exception message");
    }

    @RequestMapping(value = "/test/runtimeException")
    public int getRuntimeException() {
        return Integer.parseInt("x");
    }

    @RequestMapping(value = "/test/exception")
    public int getException() throws Exception {
        throw new Exception("Test Exception message");
    }
}

Junit Test Class

Once that was written, it was fairly easy to test the results. I used MockMVC with Spring Test to implement my test class. As part of this test, I wanted to ensure that the custom ResourceException that I created was handled correctly. I also wanted to ensure that I could handle RuntimeExceptions that happened during regular program execution could be handled. The last thing I wanted to verify was that developer created and thrown exceptions could be handled and that the message that they were created with was outputted correctly. I checked the HTTP status and the content for each type of exception.

package com.javaninja.cam.spring;


import com.javaninja.cam.JunitTestUtility;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.junit.Assert.fail;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;


/**
 * @author norris.shelton
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-test.xml")
@WebAppConfiguration
public class TestCamControllerAdvice {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    /**
     * Needed because we have some legacy code that intereactes directly with the context.  This is NOT normal and 
     * will be removed as the project is modernized. 
     * @throws Exception
     */
    @BeforeClass
    public static void createMockContext() throws Exception {
        JunitTestUtility.setup();
    }

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    /**
     * Tests the custom ResourceException and verifies that it returns a HTTP 400 with the expected message.
     */
    @Test
    public void testResourceException400() {
        try {
            mockMvc.perform(get("/test/resourceException400"))
                   .andExpect(status().isBadRequest())
                   .andExpect(content().string("Test Bad Request Exception message"))
                   .andDo(print());
        } catch (Exception e) {
            fail(e.toString());
        }
    }

    /**
     * Tests the custom ResourceException and verifies that it returns a HTTP 404 with the expected message.
     */
    @Test
    public void testResourceException404() {
        try {
            mockMvc.perform(get("/test/resourceException404"))
                   .andExpect(status().isNotFound())
                   .andExpect(content().string("Test Not Found Exception message"))
                   .andDo(print());
        } catch (Exception e) {
            fail(e.toString());
        }
    }

    /**
     * Tests that a RuntimeException is received and that it returns the expected message.
     */
    @Test
    public void testRuntimeException() {
        try {
            mockMvc.perform(get("/test/runtimeException"))
                   .andExpect(status().isInternalServerError())
                   .andExpect(content().string("java.lang.NumberFormatException: For input string: \"x\""))
                   .andDo(print());
        } catch (Exception e) {
            fail(e.toString());
        }
    }

    /**
     * Tests that an Exception is received and that it returns the expected message.
     */
    @Test
    public void testException() {
        try {
            mockMvc.perform(get("/test/exception"))
                   .andExpect(status().isInternalServerError())
                   .andExpect(content().string("Test Exception message"))
                   .andDo(print());
        } catch (Exception e) {
            fail(e.toString());
        }
    }
}

MockMVC Print Output

For readability, I also displayed the full MockMVC print output. The output makes it very clear that I received the anticipated information and nothing else. The example output is below:

com.javaninja.cam.spring.TestCamControllerAdvice

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /test/exception
       Parameters = {}
          Headers = {}

Handler:
             Type = com.javaninja.cam.spring.TestController
           Method = public int com.twinspires.cam.spring.TestController.getException() throws java.lang.Exception

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = java.lang.Exception

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

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 500
    Error message = null
          Headers = {Content-Type=, Content-Length=[22]}
     Content type = text/plain;charset=ISO-8859-1
             Body = Test Exception message
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /test/resourceException400
       Parameters = {}
          Headers = {}

Handler:
             Type = com.javaninja.cam.spring.TestController
           Method = public com.javaninja.cam.dto.Prospect com.javaninja.cam.spring.TestController.getResourceException400()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = com.javaninja.cam.exception.ResourceException

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

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 400
    Error message = null
          Headers = {Content-Type=, Content-Length=[34]}
     Content type = text/plain;charset=ISO-8859-1
             Body = Test Bad Request Exception message
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /test/resourceException404
       Parameters = {}
          Headers = {}

Handler:
             Type = com.javaninja.cam.spring.TestController
           Method = public com.javaninja.cam.dto.Prospect com.javaninja.cam.spring.TestController.getResourceException404()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = com.javaninja.cam.exception.ResourceException

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

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 404
    Error message = null
          Headers = {Content-Type=, Content-Length=[32]}
     Content type = text/plain;charset=ISO-8859-1
             Body = Test Not Found Exception message
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /test/runtimeException
       Parameters = {}
          Headers = {}

Handler:
             Type = com.javaninja.cam.spring.TestController
           Method = public int com.javaninja.cam.spring.TestController.getRuntimeException()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = java.lang.NumberFormatException

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

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 500
    Error message = null
          Headers = {Content-Type=, Content-Length=[54]}
     Content type = text/plain;charset=ISO-8859-1
             Body = java.lang.NumberFormatException: For input string: "x"
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

Process finished with exit code 0

There you have it. A full Junit/MockMVC test harness that can be used to determine if a SpringMVC controller is returning the correct information when you are trying to map Exceptions into HTTP status codes, along with messages that can be returned with the client consumer.

November 16th, 2016

Posted In: Java, java ninja, Javaninja, JUnit, MockMVC, Spring, Spring MVC, Test Driven Development

One Comment

  • […] Testing Spring MVC exceptions into status codes – Java Ninja. – Nov 16, 2016. Testing Spring MVC controllers and how they handle exceptions when they are mapped to a HTTP status code. Error message = null. A full Junit/MockMVC test harness that can be used to determine if a SpringMVC controller is returning the correct information when you are trying to map Exceptions into. […]

Leave a Reply

Your email address will not be published. Required fields are marked *

LinkedIn Auto Publish Powered By : XYZScripts.com