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

7
  • can you post the content of fooService.login? Commented Mar 25, 2017 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. Commented Mar 25, 2017 at 17:33
  • 1
    Please post the test code and the stacktrace of the NestedServletException you got. Commented Mar 25, 2017 at 17:34
  • @nanospeck then post the code of your mocking of this functionality Commented Mar 25, 2017 at 17:35
  • updated the question with mocking and stack trace Commented Mar 25, 2017 at 17:45

1 Answer 1

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")));
}
Sign up to request clarification or add additional context in comments.

3 Comments

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.
don't catch it inside the controller, rather add the throws clause in the controller method signature, so that ControllerAdvice will take care
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: github.com/nanospeck/BackbaseApp/blob/master/src/main/java/com/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.