1

I have controller with the following structure:

@RequestMapping(value = "${foo.controller.requestMappingUrl.login}", method = RequestMethod.POST)
    public ResponseMessage<String> loginUser(
            @RequestParam("username") String username, HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse) {

    try {

       return fooService.login(username); // can mock test

    } catch (UserNotFoundException e) {
            //CANNOT MOCK TEST THIS BLOCK
            String errorMsg = LoggerUtil.printStackTrace(e);
            LOG.error("[RequestId: {}] UserNotFoundException: {}, Stack: {}", requestId, e.getMessage(), errorMsg);
            httpServletResponse.setStatus(HttpStatus.NOT_ACCEPTABLE.value());
            statusCode = StatusCode.UserNotFound.value();
            responseMessage.buildResponseMessage(StringUtils.EMPTY, HttpStatus.NOT_ACCEPTABLE, statusCode,
                    messageByLocaleService.getMessageResponse(statusCode, null, locale));
    }
}

When I mock to throw the exception UserNotFoundException, I get only NestedServletException. Even though I tried adding expected = NestedServletException.class. The corbetura reports indicate that the code block is not covered in testing. Do you have any suggestion to help test the code inside the catch block.

The test code as requested:

@SuppressWarnings("unchecked")
@Test(expected = UserNotFoundException.class)
    public void testControllerUserNotFoundException() throws Exception {
        Response resp = new Response();
        resp.setStatusCode(StatusCode.UserNotFoundErrorCode);
        when(fooService.login(any(String.class)).thenThrow(UserNotFoundException.class);

        mockMvc.perform(post("/service-user/1.0/auth/login?&username=test")
                        .contentType(contentType)).andExpect(status().isNotAcceptable())
                .andExpect(jsonPath("$.statusCode", is("ERRORCODE144")));

    }

And the stack trace

java.lang.Exception: Unexpected exception, expected but was at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:28) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) Caused by: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is com.atlassian.crowd.exception.runtime.UserNotFoundException at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872) at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65) at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167) at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:155) at id.co.allianz.microservice.cop.app.auth.controller.AuthControllerTest.testControllerUserNotFoundException(AuthControllerTest.java:105) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:19) ... 22 more Caused by: com.atlassian.crowd.exception.runtime.UserNotFoundException

nanospeck
  • 3,388
  • 3
  • 36
  • 45
  • can you post the content of `fooService.login`? – rptmat57 Mar 25 '17 at 17:29
  • Sorry, cmmiiw but why do you need the service layer code since anyway I"m mocking the layer. The service layer function uses Atlassian crowd and throws UserNotFoundException which is returned from the crowd api. – nanospeck Mar 25 '17 at 17:33
  • 1
    Please post the test code and the stacktrace of the `NestedServletException` you got. –  Mar 25 '17 at 17:34
  • @nanospeck then post the code of your mocking of this functionality – rptmat57 Mar 25 '17 at 17:35
  • updated the question with mocking and stack trace – nanospeck Mar 25 '17 at 17:45
  • Its not answer to your question but it is related to some part of it hence posting it in comment- You are expecting the exception which is already catched in your controller method. In simple words your controller method catches UserNotFoundException whenever it is thrown from service method and not throwing any exception in catch block. Hence what you are trying to do here is wrong. In your test you should not expect exception but error status code. – Dhiraj Mar 26 '17 at 13:29
  • @Dhiraj I would be happy to make that change if worked. But the actual problem is the code in the catch block is not getting executed because the NesterServletExeption is thrown and the code in that area is not getting executed. – nanospeck Mar 27 '17 at 02:52

1 Answers1

4

You can't expect UserNotFoundException exception out of the controller method because you are suppressing it by simply logging and returning the response with ERRORCODE144.

The best practice here is to configure the ControllerAdvice so that all of the exceptions can be handled globally and your controller looks clean as shown below, I suggest you also look here on spring controllers exception handling.

ExceptionControllerAdvice class:

@ControllerAdvice
public class ExceptionControllerAdvice {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<String> handleUnexpectedException(UnexpectedException 
                     unExpectedExe) {

        //log and send the response back to the client here
    }
}

Controller method:

@RequestMapping(value = "${foo.controller.requestMappingUrl.login}",
                                 method = RequestMethod.POST)
public ResponseMessage<String> loginUser(
            @RequestParam("username") String username,
               HttpServletRequest httpServletRequest,
               HttpServletResponse httpServletResponse) {

       return fooService.login(username);
}

JUnit setup & test methods:

@Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    mockMvc = MockMvcBuilders.standaloneSetup(projectController)
                .setMessageConverters(new MappingJackson2HttpMessageConverter())
                .setControllerAdvice(new ExceptionControllerAdvice()).build();
}


@SuppressWarnings("unchecked")
@Test
    public void testControllerUserNotFoundException() throws Exception {
        Response resp = new Response();
        resp.setStatusCode(StatusCode.UserNotFoundErrorCode);
        when(fooService.login(any(String.class)).
        thenThrow(UserNotFoundException.class);
        mockMvc.perform(post("/service-user/1.0/auth/login?&username=test")
                        .contentType(contentType)).
     andExpect(status().isNotAcceptable())
                .andExpect(jsonPath("$.statusCode", is("ERRORCODE144")));
}
Vasu
  • 21,832
  • 11
  • 51
  • 67
  • hey, sounds like a good alternative. But how do I handle the compiler warning to surround the serviced method with a try/catch block in the Controller class. – nanospeck Mar 27 '17 at 10:24
  • don't `catch` it inside the controller, rather add the `throws` clause in the controller method signature, so that ControllerAdvice will take care – Vasu Mar 27 '17 at 11:31
  • Oopss! my bad, I was mocking the wrong Exception with duplicate name in my test class, was invoking ' import com.atlassian.crowd.exception.runtime.UserNotFoundException ' instead of ' import com.atlassian.crowd.exception.UserNotFoundException ' . Accepting this answer because I found this elegant way of implementing would have helped avoid my error by helping identify it early. I have written a simple sample here: https://github.com/nanospeck/BackbaseApp/blob/master/src/main/java/com/backbase/exception/ExceptionControllerAdvice.java – nanospeck Mar 29 '17 at 03:01