Java 弹簧控制器捕捉块中的单元测试代码
我有以下结构的控制器:Java 弹簧控制器捕捉块中的单元测试代码,java,spring,unit-testing,junit,mockito,Java,Spring,Unit Testing,Junit,Mockito,我有以下结构的控制器: @RequestMapping(value = "${foo.controller.requestMappingUrl.login}", method = RequestMethod.POST) public ResponseMessage<String> loginUser( @RequestParam("username") String username, HttpServletRequest httpServletReq
@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));
}
}
和堆栈跟踪
java.lang.Exception:意外异常,
预期
但是在
org.junit.internal.runners.statements.ExpectException.evaluateExpectException.java:28
在
org.junit.internal.runners.statements.RunBefores.evaluateRunBefores.java:26
在
org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluateRunBeforeTestMethodCallbacks.java:75
在
org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.EvaluaterUnterTestMethodCallbacks.java:86
在
org.springframework.test.context.junit4.statements.SpringRepeat.evaluateSpringRepeat.java:84
位于org.junit.runners.ParentRunner.runLeafParentRunner.java:325处
org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChildSpringJUnit4ClassRunner.java:252
在
org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChildSpringJUnit4ClassRunner.java:94
位于org.junit.runners.ParentRunner$3.runParentRunner.java:290处
org.junit.runners.ParentRunner$1.scheduleParentRunner.java:71 at
org.junit.runners.ParentRunner.runChildrenParentRunner.java:288 at
org.junit.runners.ParentRunner.access$000ParentRunner.java:58
org.junit.runners.ParentRunner$2.evaluateParentRunner.java:268
org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluaterUnbeforetClassCallbacks.java:61
在
org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.EvaluaterUnterTestClassCallbacks.java:70
位于org.junit.runners.ParentRunner.runParentRunner.java:363
org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runSpringJUnit4ClassRunner.java:191
在
org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.runJUnit4TestReference.java:50
在
org.eclipse.jdt.internal.junit.runner.TestExecution.runTestExecution.java:38
在
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTestsRemoteTestRunner.java:467
在
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTestsRemoteTestRunner.java:683
在
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runRemoteTestRunner.java:390
在
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.mainRemoteTestRunner.java:197
原因:org.springframework.web.util.NestedServletException:
请求处理失败;嵌套异常是
com.atlassian.crowd.exception.runtime.UserNotFoundException位于
org.springframework.web.servlet.FrameworkServlet.processRequestFrameworkServlet.java:982
在
org.springframework.web.servlet.FrameworkServlet.doPostFrameworkServlet.java:872
在javax.servlet.http.HttpServlet.serviceHttpServlet.java:648 at
org.springframework.web.servlet.FrameworkServlet.serviceFrameworkServlet.java:846
在
org.springframework.test.web.servlet.TestDispatcherServlet.serviceTestDispatcherServlet.java:65
在javax.servlet.http.HttpServlet.serviceHttpServlet.java:729 at
org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilterMockFilterChain.java:167
在
org.springframework.mock.web.MockFilterChain.doFilterMockFilterChain.java:134
在
org.springframework.test.web.servlet.MockMvc.performMockMvc.java:155
在
id.co.allianz.microservice.cop.app.auth.controller.AuthControllerTest.TestControllerUserNotFoundExceptionOnAuthControllerTest.java:105
在sun.reflect.NativeMethodAccessorImpl.invoke0Native方法在
sun.reflect.NativeMethodAccessorImpl.invokeNativeMethodAccessorImpl.java:62
在
sun.reflect.DelegatingMethodAccessorImpl.invokeDelegatingMethodAccessorImpl.java:43
java:498 at
org.junit.runners.model.FrameworkMethod$1.runReflectVeCallFrameworkMethod.java:50
在
org.junit.internal.runners.model.ReflectiveCallable.runReflectiveCallable.java:12
在
org.junit.runners.model.FrameworkMethod.invokeeexplosivelyframeworkmethod.java:47
在
org.junit.internal.runners.statements.InvokeMethod.evaluateInvokeMethod.java:17
在
org.junit.internal.runners.statements.ExpectException.evaluateExpectException.java:19
... 22多宗由以下原因引起:
com.atlassian.crowd.exception.runtime.UserNotFoundException
您不能期望控制器方法中出现UserNotFoundException异常,因为您只是通过记录并返回带有ERRORCODE144的响应来抑制它
这里的最佳实践是配置ControllerAdvice,以便可以全局处理所有异常,并且您的控制器看起来干净,如下所示。我建议您也查看spring控制器异常处理
例外情况ControllerAdvice类:
您不能期望控制器方法中出现UserNotFoundException异常,因为您只是通过记录并返回带有ERRORCODE144的响应来抑制它
我认为这里的最佳实践是
要配置ControllerAdvice,以便可以全局处理所有异常,并且您的控制器看起来干净,如下图所示,我建议您也查看spring控制器异常处理
例外情况ControllerAdvice类:
你能发布fooService.login的内容吗?对不起,cmmiiw,既然我在模拟服务层,为什么你需要服务层代码呢。服务层函数使用Atlassian crowd并抛出从群组api返回的UserNotFoundException。请发布您得到的NestedServletException的测试代码和堆栈跟踪。@nanospeck然后发布您对该功能的模拟代码使用模拟和堆栈跟踪更新问题您可以发布fooService.login?对不起,cmmiiw,既然我在模仿服务层,为什么你需要服务层代码呢。服务层函数使用Atlassian crowd并抛出从群组api返回的UserNotFoundException。请发布您得到的NestedServletException的测试代码和stacktrace。@nanospeck然后发布您模拟此功能的代码使用mocking和stack tracehey更新问题,听起来是个不错的选择。但是我如何处理编译器警告,在控制器类中用try/catch块包围服务方法。不要在控制器中捕获它,而是在控制器方法签名中添加throws子句,这样ControllerAdvice就会小心了!糟糕的是,我在测试类中模拟了名称重复的错误异常,调用了“import com.atlassian.crowd.Exception.runtime.UserNotFoundException”而不是“import com.atlassian.crowd.Exception.UserNotFoundException”。接受这个答案是因为我发现这种优雅的实现方式有助于尽早识别错误,从而避免了我的错误。我在这里写了一个简单的例子:嘿,听起来是个不错的选择。但是我如何处理编译器警告,在控制器类中用try/catch块包围服务方法。不要在控制器中捕获它,而是在控制器方法签名中添加throws子句,这样ControllerAdvice就会小心了!糟糕的是,我在测试类中模拟了名称重复的错误异常,调用了“import com.atlassian.crowd.Exception.runtime.UserNotFoundException”而不是“import com.atlassian.crowd.Exception.UserNotFoundException”。接受这个答案是因为我发现这种优雅的实现方式有助于尽早识别错误,从而避免了我的错误。我在这里写了一个简单的示例:
@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")));
}
@ControllerAdvice
public class ExceptionControllerAdvice {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUnexpectedException(UnexpectedException
unExpectedExe) {
//log and send the response back to the client here
}
}
@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);
}
@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")));
}