我在应用程序中有一组控制器和一个注释为
@ControllerAdvice
的类,它设置每个控制器中使用的某些数据元素。我正在使用 Spring MVC 3.2
并为这些控制器提供了 Junits。当我运行 Junit 时,控件不会转到 ControllerAdvice
类,如果我在 Tomcat
中部署应用程序并通过浏览器提交请求,它可以正常工作。
请问有什么想法吗?
使用@eugene-to和另一个类似的答案后here我发现了局限性并在Spring上提出了一个问题:https://github.com/spring-projects/spring-framework/issues/17348
因此,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;
}
在尝试测试用
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
配置我的测试通过了。
此代码对我有用:
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 文档:
简而言之,如果您只是测试控制器及其方法,那么您可以使用 '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;
// ...
}
@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" />
(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
尝试将以下内容添加到您的测试 applicationContext.xml (或等效的 spring 配置文件,如果您使用的话)。
<context:component-scan base-package="com.example.path.to.package" />
或者,您可能需要通过在测试类之前包含以下注释来“手动”加载测试中的上下文:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")
祝你好运!
我必须改变这一点
@AutoConfigureMockMvc
@ContextConfiguration(classes = OrderController.class)
@WebMvcTest
class OrdersIntegrationTest
对此:
@AutoConfigureMockMvc
@ContextConfiguration(classes = {OrderController.class, OrdersExceptionHandler.class})
@WebMvcTest
class OrdersIntegrationTest
@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 = "[email protected]", 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());
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MyController.class, MyControllerAdvice.class})
@AutoConfigureMockMvc(secure = false)
@ContextConfiguration(classes = {MyTestConfig.class})
@EnableWebMvc