无法处理集成测试中的预期异常



我无法通过测试notFoundFoo因为我得到了NestedServletException而不是预期的FooNotFoundException异常。

我在考试中做错了什么?

控制器:

@RestController
@RequestMapping("/api/foo")
public class FooController {
@Autowired
private FooService fooService;
@GetMapping("/{fooId}/status")
@ResponseStatus(HttpStatus.OK)
public PaymentStatus retrievePaymentStatus(@PathVariable String fooId) {
PaymentStatus paymentStatus = fooService.retrievePaymentStatus(fooId);
if (paymentStatus == null) {
throw new InvoiceNotFoundException();
}
// Preconditions.checkNotNull(paymentStatus, new FooNotFoundException());
return paymentStatus;
}
}

FooNotFoundException:

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Foo not found")
public class FooNotFoundException extends RuntimeException {
public FooNotFoundException() {
}
public FooNotFoundException(String message) {
super(message);
}
}

测试:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
@AutoConfigureMockMvc
public class FooControllerTest {
@Autowired
FooRepository fooRepository;
@Autowired
BloomFilterManager bloomFilterManager;
@Autowired
private MockMvc mockMvc;
private MediaType CONTENT_TYPE = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
@Test
public void foundFooWithPaymentStatus() throws Exception {
Faker faker = new Faker();
Foo.PaymentStatus[] paymentStatuses = Foo.PaymentStatus.values();
Foo foo= fooRepository.save(new Foo(
faker.number().randomNumber(),
faker.lorem().word(),
faker.number().randomDigit(),
new BigDecimal(Math.random()),
paymentStatuses[faker.number().numberBetween(0, paymentStatuses.length)],
faker.date().between(new Date(), DateUtils.addDays(new Date(), 13))
));
bloomFilterManager.getBloomFilter().put(foo.getId());
RequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/api/foo/{fooId}/status", foo.getId())
.accept(CONTENT_TYPE);
MvcResult result = mockMvc.perform(requestBuilder)
.andExpect(status().isOk())
.andExpect(content().contentType(CONTENT_TYPE))
.andReturn();
String expected = """ + foo.getPaymentStatus() + """;
assertEquals(expected, result.getResponse().getContentAsString());
}
@Test(expected = FooNotFoundException.class)
public void notFoundFoo() throws Exception {
String fooId = "100000000000000";
RequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/api/foo/{fooId}/status", fooId)
.accept(CONTENT_TYPE);
mockMvc.perform(requestBuilder)
.andExpect(status().isNotFound())
.andExpect(content().contentType(CONTENT_TYPE));
}

输出:

java.lang.Exception: Unexpected exception, expected<org.learn.foo.exception.FooNotFoundException> but was<org.springframework.web.util.NestedServletException>
at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:28)
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.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
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.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException: org.learn.foo.exception.FooNotFoundException
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
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.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:155)
at org.learn.integration.FooControllerTest.notFoundFoo(FooControllerTest.java:100)
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)
... 28 more
Caused by: java.lang.NullPointerException: org.learn.foo.exception.FooNotFoundException
at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:900)
at org.learn.foo.controller.FooController.retrievePaymentStatus(FooController.java:22)
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.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
... 58 more

更新:

MockHttpServletRequest:
HTTP Method = GET
Request URI = /api/foo/100000000000000/status
Parameters = {}
Headers = {Accept=[application/json;charset=UTF-8]}
Handler:
Type = org.wixanz.omniva.foo.controller.FooController
Method = public org.wixanz.omniva.foo.domain.foo$PaymentStatus org.wixanz.omniva.foo.controller.FooController.retrievePaymentStatus(java.lang.String)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.wixanz.omniva.foo.exception.InvoiceNotFoundException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = Foo not found
Headers = {}
Content type = null
Body = 
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.Exception: Unexpected exception, expected<org.wixanz.omniva.foo.exception.FooNotFoundException> but was<java.lang.AssertionError>
at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:28)
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.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
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.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.AssertionError: Content type not set
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:35)
at org.springframework.test.util.AssertionErrors.assertTrue(AssertionErrors.java:65)
at org.springframework.test.web.servlet.result.ContentResultMatchers$1.match(ContentResultMatchers.java:81)
at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
at org.wixanz.omniva.integration.FooControllerTest.notFoundFoo(FooControllerTest.java:87)
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)
... 28 more

>NestedServletException只包装异常的原因。
这不是您的实际问题。

它的javadoc将自己定义为:

ServletException 的子类,用于正确处理根本原因 消息和堆栈跟踪术语,就像 NestedChecked/RuntimeException 确实如此。

在 rest 控制器中引发的异常必须映射到 http 响应。 如果你没有这样做,Spring 会根据异常性质(检查或运行时)抛出NestedServletException/NestedCheckedException来为你做到这一点。

Java 异常不是 http 响应。 你不能让它像在经典的 java 代码中那样被抛出。 您必须将其转换为与http规范兼容的东西。

对于 Rest 控制器,您应该使用@ExceptionHandler@ControllerAdvice

@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(FooNotFoundException.class)
protected ResponseEntity<Object> handleFooNotFound(FooNotFoundException exception, WebRequest request) {
return handleExceptionInternal(exception, "Foo Not Found", 
new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}
}

最新更新