我有一天遇到了一個問題,那裏有一個@Valid註釋被意外地從控制器類中刪除。不幸的是,它沒有破壞我們的任何測試。我們的單元測試都沒有實際執行Spring AnnotationMethodHandlerAdapter
通路。我們只是直接測試我們的控制器類。測試Spring @MVC註解
如何編寫一個單元或集成測試,如果我的@MVC註釋錯誤會正確失敗?有沒有一種方法可以讓Spring用MockHttpServlet或其他東西來找到並運用相關的控制器?
我有一天遇到了一個問題,那裏有一個@Valid註釋被意外地從控制器類中刪除。不幸的是,它沒有破壞我們的任何測試。我們的單元測試都沒有實際執行Spring AnnotationMethodHandlerAdapter
通路。我們只是直接測試我們的控制器類。測試Spring @MVC註解
如何編寫一個單元或集成測試,如果我的@MVC註釋錯誤會正確失敗?有沒有一種方法可以讓Spring用MockHttpServlet或其他東西來找到並運用相關的控制器?
我的博客文章在即將到來的SPRIN g 3.2(SNAPSHOT可用)或者使用spring-test-mvc(https://github.com/SpringSource/spring-test-mvc)你可以這樣做:
首先我們模擬驗證,因爲我們不想要測試驗證器,只是想知道是否調用驗證。
public class LocalValidatorFactoryBeanMock extends LocalValidatorFactoryBean
{
private boolean fakeErrors;
public void fakeErrors ()
{
this.fakeErrors = true;
}
@Override
public boolean supports (Class<?> clazz)
{
return true;
}
@Override
public void validate (Object target, Errors errors, Object... validationHints)
{
if (fakeErrors)
{
errors.reject("error");
}
}
}
這是我們的測試類:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration
public class RegisterControllerTest
{
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Autowired
@InjectMocks
private RegisterController registerController;
@Autowired
private LocalValidatorFactoryBeanMock validator;
@Before
public void setup ()
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
// if you want to inject mocks into your controller
MockitoAnnotations.initMocks(this);
}
@Test
public void testPostValidationError () throws Exception
{
validator.fakeErrors();
MockHttpServletRequestBuilder post = post("/info/register");
post.param("name", "Bob");
ResultActions result = getMockMvc().perform(post);
// no redirect as we have errors
result.andExpect(view().name("info/register"));
}
@Configuration
@Import(DispatcherServletConfig.class)
static class Config extends WebMvcConfigurerAdapter
{
@Override
public Validator getValidator ()
{
return new LocalValidatorFactoryBeanMock();
}
@Bean
RegisterController registerController ()
{
return new RegisterController();
}
}
}
當然。沒有理由說明爲什麼你的測試不能實例化自己的DispatcherServlet
,爲它注入容器中的各種項目(例如ServletContext
),包括上下文定義文件的位置。
爲了這個目的,Spring帶有各種與servlet相關的MockXYZ
類,包括MockServletContext
,MockHttpServletRequest
和MockHttpServletResponse
。在通常意義上,它們並不是真正的「模擬」對象,它們更像是愚蠢的存根,但它們完成了這項工作。
該servlet的測試上下文將具有通常與MVC相關的bean以及要測試的bean。一旦servlet被初始化,創建模擬請求和響應,並將它們提供給servet的方法service()
。如果請求被正確路由,您可以檢查寫入模擬響應的結果。
我寫這種東西的集成測試。假設你有驗證註釋一個bean:
public class MyForm {
@NotNull
private Long myNumber;
...
}
和處理提交
@Controller
@RequestMapping("/simple-form")
public class MyController {
private final static String FORM_VIEW = null;
@RequestMapping(method = RequestMethod.POST)
public String processFormSubmission(@Valid MyForm myForm,
BindingResult result) {
if (result.hasErrors()) {
return FORM_VIEW;
}
// process the form
return "success-view";
}
}
控制器和你想測試的@Valid和@NotNull註解正確的接線方式:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"file:web/WEB-INF/application-context.xml",
"file:web/WEB-INF/dispatcher-servlet.xml"})
public class MyControllerIntegrationTest {
@Inject
private ApplicationContext applicationContext;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HandlerAdapter handlerAdapter;
@Before
public void setUp() throws Exception {
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
}
ModelAndView handle(HttpServletRequest request, HttpServletResponse response)
throws Exception {
final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
// if you want to override any injected attributes do it here
final HandlerInterceptor[] interceptors =
handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
final ModelAndView mav = handlerAdapter.handle(request, response, controller);
return mav;
}
@Test
public void testProcessFormSubmission() throws Exception {
request.setMethod("POST");
request.setRequestURI("/simple-form");
request.setParameter("myNumber", "");
final ModelAndView mav = handle(request, response);
// test we're returned back to the form
assertViewName(mav, "simple-form");
// make assertions on the errors
final BindingResult errors = assertAndReturnModelAttributeOfType(mav,
"org.springframework.validation.BindingResult.myForm",
BindingResult.class);
assertEquals(1, errors.getErrorCount());
assertEquals("", errors.getFieldValue("myNumber"));
}
這是friggin'真棒。這正是我尋找的東西!謝謝! – 2010-07-29 18:19:30
你不會進行單元測試的註解,你會嗎?似乎是我對整合測試的關注。 – 2010-02-23 12:15:32