DavyJohnes
DavyJohnes

Reputation: 321

Spring Boot Testing: exception in REST controller

I have a Spring Boot application and want to cover my REST controllers by integration test. Here is my controller:

@RestController
@RequestMapping("/tools/port-scan")
public class PortScanController {
    private final PortScanService service;

    public PortScanController(final PortScanService portScanService) {
        service = portScanService;
    }

    @GetMapping("")
    public final PortScanInfo getInfo(
            @RequestParam("address") final String address,
            @RequestParam(name = "port") final int port)
            throws InetAddressException, IOException {
        return service.scanPort(address, port);
    }
}

In one of test cases I want to test that endpoint throws an exception in some circumstances. Here is my test class:

@RunWith(SpringRunner.class)
@WebMvcTest(PortScanController.class)
public class PortScanControllerIT {
    @Autowired
    private MockMvc mvc;

    private static final String PORT_SCAN_URL = "/tools/port-scan";

    @Test
    public void testLocalAddress() throws Exception {
        mvc.perform(get(PORT_SCAN_URL).param("address", "192.168.1.100").param("port", "53")).andExpect(status().isInternalServerError());
    }
}

What is the best way to do that? Current implementation doesn't handle InetAddressException which is thrown from PortScanController.getInfo() and when I start test, I receive and error:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is com.handytools.webapi.exceptions.InetAddressException: Site local IP is not supported

It is not possible to specify expected exception in @Test annotation since original InetAddressException is wrapped with NestedServletException.

Upvotes: 4

Views: 16462

Answers (4)

Ibrahima Sory TRAORE
Ibrahima Sory TRAORE

Reputation: 203

I had the same issue and i fix it with org.assertj.core.api.Assertions.assertThatExceptionOfType :

@Test
public void shouldThrowInetAddressException() {
    assertThatExceptionOfType(InetAddressException.class)
        .isThrownBy(() -> get(PORT_SCAN_URL).param("address", "192.168.1.100").param("port", "53"));
}

I hope it's help you !

Upvotes: 0

borowis
borowis

Reputation: 1235

You could define an exception handler

@ExceptionHandler(InetAddressException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public Response handledInvalidAddressException(InetAddressException e)
{
    log e
    return getValidationErrorResponse(e);
}

and then in your test you could do

mvc.perform(get(PORT_SCAN_URL)
   .param("address", "192.168.1.100")
   .param("port", "53"))
   .andExpect(status().isBadRequest())
   .andExpect(jsonPath("$.response").exists())
   .andExpect(jsonPath("$.response.code", is(400)))
   .andExpect(jsonPath("$.response.errors[0].message", is("Site local IP is not supported")));

Upvotes: 2

Vasu
Vasu

Reputation: 22384

In order to test the wrapped exception (i.e., InetAddressException), you can create a JUnit Rule using ExpectedException class and then set the expectMessage() (received from NestedServletException's getMessage(), which contains the actual cause), you can refer the below code for the same:

@Rule
public ExpectedException inetAddressExceptionRule = ExpectedException.none();

@Test
public void testLocalAddress() {

    //Set the message exactly as returned by NestedServletException
    inetAddressExceptionRule.expectMessage("Request processing failed; nested exception is com.handytools.webapi.exceptions.InetAddressException: Site local IP is not supported");

     //or you can check below for actual cause 
     inetAddressExceptionRule.expectCause(org.hamcrest.Matchers.any(InetAddressException.class))

    //code for throwing InetAddressException here (wrapped by Spring's NestedServletException)
}

You can refer the ExpectedException API here:

http://junit.org/junit4/javadoc/4.12/org/junit/rules/ExpectedException.html

Upvotes: 4

Maciej Walkowiak
Maciej Walkowiak

Reputation: 12932

Spring Boot Test package comes with AssertJ that has very convenient way of verifying thrown exceptions.

To verify cause:

@Test
public void shouldThrowException() {
    assertThatThrownBy(() -> methodThrowingException()).hasCause(InetAddressException .class);
}

There are also few more methods that you may be interested in. I suggest having a look in docs.

Upvotes: 10

Related Questions