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

时间:2018-02-22 10:49:51

标签: java spring-boot exception integration-testing

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

1 个答案:

答案 0 :(得分:0)

NestedServletException仅包含异常原因 这不是你的实际问题。

它的javadoc将自己定义为:

  

正确处理根本原因的ServletException的子类   消息和堆栈跟踪的条款,就像   NestedChecked / RuntimeException确实。

休息控制器中抛出的异常必须映射到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);
    }
}