84

我在应用程序中有一组控制器和一个注释类,@ControllerAdvice它设置了在每个控制器中使用的某些数据元素。我正在Spring MVC 3.2为这些控制器使用并拥有 Junits。当我运行 Junit 时,如果我在其中部署应用程序并通过浏览器提交请求,控件将不会进入ControllerAdvice它工作正常的类。Tomcat

请问有什么想法吗?

4

13 回答 13

129

在使用@eugene-to 的答案和另一个类似的答案后,发现了限制并在 Spring 上提出了一个问题:https ://jira.spring.io/browse/SPR-12751

因此,Spring test@ControllerAdvice在 4.2 中引入了在构建器中注册类的能力。如果您使用的是Spring Boot,那么您将需要 1.3.0 或更高版本。

通过这项改进,如果您使用独立设置,则可以设置一个或多个ControllerAdvice实例,如下所示:

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

注意:该名称setControllerAdvice()可能不会立即显而易见,但您可以将许多实例传递给它,因为它具有 var-args 签名。

于 2015-02-25T19:24:44.313 回答
42

假设你有一个用@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;
}
于 2013-10-12T19:39:23.017 回答
24

我在尝试ExceptionHandler@ControllerAdvice. 就我而言,我必须在测试类上添加@Configuration带有@EnableWebMvc注释的文件。@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配置,我的测试通过了。

于 2013-09-20T07:38:49.527 回答
19

这段代码对我有用:

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));
    }
}
于 2017-07-11T13:28:37.707 回答
7

很长一段时间以来,我一直在为同样的事情苦苦挣扎。经过大量挖掘,最好的参考是 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;

    // ...

}
于 2013-04-08T10:27:51.190 回答
4

应该由ControllerAdvice挑选@WebMvcTest,另请参阅Spring-Doc Works so far for me。

例子:

@RunWith(SpringRunner.class)
@WebMvcTest(ProductViewController.class)
于 2020-03-10T09:06:13.423 回答
1

@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" />
于 2014-08-20T18:46:38.930 回答
1

我在使用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
于 2017-10-30T04:50:43.457 回答
0

您需要提供更多信息,可能还需要提供一些实际代码和/或配置文件,然后才能获得具体答案。也就是说,根据您提供的信息,听起来带注释的 bean 没有被加载。

尝试将以下内容添加到您的测试 applicationContext.xml (或等效的 spring 配置文件,如果您正在使用)。

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

或者,您可能需要通过在测试类之前包含以下注释来“手动”加载测试中的上下文:

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

祝你好运!

于 2013-03-08T20:22:56.237 回答
0

我怀疑您需要在测试中使用 asyncDispatch;异步控制器破坏了常规测试框架。

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

于 2019-05-17T08:45:11.373 回答
0

我正在使用 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());
    }
}
于 2021-05-29T05:44:16.143 回答
0

最简单的方法是将您的@ControllerAdvice 注释类添加到@ContextConfiguration。

我不得不改变

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

对此:

@AutoConfigureMockMvc
@ContextConfiguration(classes = {OrderController.class, OrdersExceptionHandler.class})
@WebMvcTest
class OrdersIntegrationTest
于 2020-12-29T15:20:17.640 回答
-1

刚遇到同样的问题,但通过将建议器添加到@SpringBootTest 中的类来解决它:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MyController.class, MyControllerAdvice.class})
@AutoConfigureMockMvc(secure = false)
@ContextConfiguration(classes = {MyTestConfig.class})
@EnableWebMvc
于 2021-09-17T12:51:46.543 回答