【问题标题】:Cannot handle expected exception in integration test无法处理集成测试中的预期异常
【发布时间】:2018-02-22 10:49:50
【问题描述】:

我无法通过测试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

【问题讨论】:

    标签: java spring-boot exception integration-testing


    【解决方案1】:

    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);
        }
    }
    

    【讨论】:

    • 我更新了我的帖子,添加了控制器表示。 Preconditions.checkNotNull 如果值为 null 则调用 FooNotFoundException 异常。
    • 据我所知,它不会影响任何事情。我已经评论了先决条件并通过 if 语句检查了 nulll,然后我设法通过了预期的异常,但由于某种原因,响应中缺少内容类型。任何想法为什么会这样?
    • ResponseStatus 可能不是 Rest Controller 的正确选择。试试@ControllerAdvice
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-23
    • 2019-05-29
    • 1970-01-01
    • 2015-07-31
    • 2020-12-16
    • 2012-04-05
    相关资源
    最近更新 更多