簡體   English   中英

Spring MVC 控制器單元測試不調用@ControllerAdvice

[英]Spring MVC Controllers Unit Test not calling @ControllerAdvice

我在應用程序中有一組控制器和一個注釋為@ControllerAdvice的類,它設置了在每個控制器中使用的某些數據元素。 我正在使用Spring MVC 3.2並為這些控制器安裝了 Junit。 當我運行 Junit 時,控件不會轉到ControllerAdvice類,如果我在Tomcat部署應用程序並通過瀏覽器提交請求,它可以正常工作。

請問有什么想法嗎?

使用答案來自@尤金到和其他類似的一個后,在這里我找到了限制,並在春季提出的一個問題: https://jira.spring.io/browse/SPR-12751

因此,Spring 測試在 4.2 中引入了在構建器中注冊@ControllerAdvice類的功能。 如果您使用的是Spring Boot,那么您將需要 1.3.0 或更高版本。

通過這一改進,如果您使用獨立設置,那么您可以像這樣設置一個或多個ControllerAdvice實例:

mockMvc = MockMvcBuilders.standaloneSetup(yourController)
            .setControllerAdvice(new YourControllerAdvice())
            .build();

注意:名稱setControllerAdvice()可能不會立即顯而易見,但您可以將許多實例傳遞給它,因為它具有 var-args 簽名。

假設您有用@ControllerAdvice 注釋的MyControllerAdvice 類,該類具有用@ExceptionHandler 注釋的方法。 對於 MockMvc,您可以輕松地將此類添加為異常解析器。

@Before
public void beforeTest() {
    MockMvc mockMvc = standaloneSetup(myControllers)
        .setHandlerExceptionResolvers(createExceptionResolver())
        .build();
}

private ExceptionHandlerExceptionResolver createExceptionResolver() {
    ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
        protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
            Method method = new ExceptionHandlerMethodResolver(MyControllerAdvice.class).resolveMethod(exception);
            return new ServletInvocableHandlerMethod(new MyControllerAdvice(), method);
        }
    };
    exceptionResolver.afterPropertiesSet();
    return exceptionResolver;
}

我在嘗試測試用@ControllerAdvice注釋的ExceptionHandler時遇到了類似的問題。 在我的情況下,我必須將帶有@EnableWebMvc注釋的@Configuration文件添加到測試類的@ContextConfiguration

所以我的測試是這樣的:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {
  RestProcessingExceptionHandler.class,
  TestConfiguration.class,
  RestProcessingExceptionThrowingController.class })
public class TestRestProcessingExceptionHandler {

  private MockMvc mockMvc;
  @Autowired
  WebApplicationContext wac;

  @Before
  public void setup() {
    mockMvc = webAppContextSetup(wac).build();
  }

  @Configuration
  // !!! this is very important - conf with this annotation 
  //     must be included in @ContextConfiguration
  @EnableWebMvc
  public static class TestConfiguration { }

  @Controller
  @RequestMapping("/tests")
  public static class RestProcessingExceptionThrowingController {
    @RequestMapping(value = "/exception", method = GET)
    public @ResponseBody String find() {
      throw new RestProcessingException("global_error_test");
    }
  }

  @Test
  public void testHandleException() throws Exception {
    mockMvc.perform(get("/tests/exception"))
      .andExpect(new ResultMatcher() {
        @Override
        public void match(MvcResult result) throws Exception {
          result.getResponse().getContentAsString().contains("global_error_test");
        }
      })
      .andExpect(status().isBadRequest());
  }
}

使用@EnableWebMvc配置我的測試通過了。

這段代碼對我有用:

public class MyGlobalExceptionHandlerTest {

    private MockMvc mockMvc;

    @Mock
    HealthController healthController;

    @BeforeTest
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(healthController)
            .setControllerAdvice(new GlobalExceptionHandler())
            .build();
    }

    @Test(groups = { "services" })
    public void testGlobalExceptionHandlerError() throws Exception {
        Mockito.when(healthController.health())]
               .thenThrow(new RuntimeException("Unexpected Exception"));
        mockMvc.perform(get("/health")).andExpect(status().is(500));
    }
}

很長一段時間以來,我一直在為同樣的事情苦苦掙扎。 經過大量挖掘,最好的參考是 Spring 文檔:

http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/testing.html#spring-mvc-test-framework

簡而言之,如果您只是測試控制器及其方法,那么您可以使用“standaloneSetup”方法來創建一個簡單的 Spring MVC 配置。 不會包括您使用 @ControllerAdvice 注釋的錯誤處理程序。

private MockMvc mockMvc;

@Before
public void setup() {
    this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
}

// ...

要創建一個更完整的Spring MVC的配置不會包含您的錯誤處理程序,你應該使用以下設置:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("test-servlet-context.xml")
public class AccountTests {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Autowired
    private AccountService accountService;

    // ...

}

@tunguski 示例代碼有效,但了解事情的工作原理是值得的。 這只是一種設置方式。

@EnableWebMvc相當於在spring配置文件中跟隨

<mvc:annotation-driven />

本質上,為了工作,您需要初始化 Spring Mvc 並加載所有控制器和 bean 引用。 所以以下可能是一個有效的設置以及替代

以下是您將如何設置測試類

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath: "classpath:test-context.xml" })
    @WebAppConfiguration    
    public class BaseTest {

        @Autowired
        WebApplicationContext wac;

        private MockMvc mockMvc;

        @Before
        public void setUp()  {
            mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        }
    }

以下可能是測試的彈簧配置

<mvc:annotation-driven />
<context:component-scan base-package="com.base.package.controllers" />

我在用spock (groovy) 編寫控制器測試時遇到了這個問題。 我的測試類最初是這樣寫的:

@AutoConfigureMockMvc(secure = false)
@SpringBootTest
@Category(RestTest)
class FooControllerTest extends Specification {
  def fooService = Mock(FooService)
  def underTest = new FooController(FooService)
  def mockMvc = MockMvcBuilders.standaloneSetup(underTest).build()
....
}

這導致 ControllerAdvice 被忽略。 將代碼更改為 Autowire 模擬解決了問題。

@AutoConfigureMockMvc(secure = false)
@SpringBootTest
@Category(RestTest)
class FooControllerTest extends Specification {

  @AutowiredMock
  FooService FooService

  @Autowired
  MockMvc mockMvc

ControllerAdvice應該由@WebMvcTest ,到目前為止,我還可以參閱Spring-Doc Works。

例子:

@RunWith(SpringRunner.class)
@WebMvcTest(ProductViewController.class)

您需要提供更多信息,也許還有一些實際的代碼和/或配置文件,然后才能得到具體的答案。 也就是說,根據您提供的一點信息,聽起來好像沒有加載帶注釋的 bean。

嘗試將以下內容添加到您的測試 applicationContext.xml(或等效的 spring 配置文件,如果您正在使用)。

<context:component-scan base-package="com.example.path.to.package" />

或者,您可能需要通過在測試類之前包含以下注釋來“手動”加載測試中的上下文:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")

祝你好運!

我懷疑您需要在測試中使用 asyncDispatch; 常規測試框架被異步控制器破壞了。

嘗試以下方法: https : //github.com/spring-projects/spring-framework/blob/master/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests .java

最簡單的方法是將您的@ControllerAdvice 注釋類添加到@ContextConfiguration。

我不得不改變這個

@AutoConfigureMockMvc
@ContextConfiguration(classes = OrderController.class)
@WebMvcTest
class OrdersIntegrationTest

對此:

@AutoConfigureMockMvc
@ContextConfiguration(classes = {OrderController.class, OrdersExceptionHandler.class})
@WebMvcTest
class OrdersIntegrationTest

我正在使用 Spring Boot 2.x,但似乎不再需要 MockMvcBuilders,或者當我們將 ControllerAdvice 定義為配置的一部分時,它會被加載。

@WebMvcTest
@ContextConfiguration(classes = {
  UserEndpoint.class, //the controller class for test
  WebConfiguration.class, //security configurations, if any
  StandardRestExceptionInterpreter.class. //<-- this is the ControllerAdvice class
})
@WithMockUser(username = "test@asdf.com", authorities = {"DEFAULT"})
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class UserEndpointTests {

@Test
@Order(3)
public void shouldThrowExceptionWhenRegisteringDuplicateUser() throws Exception {
    //do setup...
    Mockito.doThrow(EntityExistsException.class).when(this.userService).register(user);

    this.mockMvc
            .perform(MockMvcRequestBuilders
                    .post("/users")
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(this.objectMapper.writeValueAsString(user)))
            .andDo(MockMvcResultHandlers.print())
            .andExpect(MockMvcResultMatchers.status().isConflict());
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM