2012-09-06 14 views
11

私は、Hibernate Validatorアノテーションで注釈を付けたBrowserというPOJOを持っています。JUnitテストでSpring MVCが検証を呼び出す方法を教えてください。

import org.hibernate.validator.constraints.NotEmpty; 

public class Browser { 

    @NotEmpty 
    private String userAgent; 
    @NotEmpty 
    private String browserName; 

... 

} 

コントローラメソッドが検証エラーをキャッチすることを確認しようとする次の単体テストを作成しました。エラーを持っていることはありません

@RequestMapping(value = "/browser/create", method = RequestMethod.POST) 
public String add(@Valid Browser browser, BindingResult result, HttpServletRequest request) throws Exception { 
    if (result.hasErrors()) { 
     request.setAttribute("errorMessage", result.getAllErrors()); 
     return VIEW_NAME; 
    } 

    browserManager.save(browser); 

    request.getSession(false).setAttribute("successMessage", 
      String.format("Browser %s added successfully.", browser.getUserAgent())); 

    return "redirect:/" + VIEW_NAME; 
} 

私が経験してる問題はその結果であるので、@Validが認識取得されていないようなものだ:

@Test 
public void testInvalidData() throws Exception { 
    Browser browser = new Browser("opera", null); 
    MockHttpServletRequest request = new MockHttpServletRequest(); 

    BindingResult errors = new DataBinder(browser).getBindingResult(); 
    // controller is initialized in @Before method 
    controller.add(browser, errors, request); 
    assertEquals(1, errors.getErrorCount()); 
} 

は、ここに私のコントローラのadd()メソッドです。私はテストクラスに以下を追加しようとしましたが、問題を解決しません。

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration({"file:path-to/WEB-INF/spring-mvc-servlet.xml"}) 

JUnitでテストするときに@Validを認識(および検証)する方法を知っている人はいますか?

おかげで、

マット

答えて

3

検証は、コントローラへの呼び出しの前に行われているので、あなたのテストは、この検証を呼び出していません。

コントローラをテストする別の方法があります。ここでは、コントローラを直接呼び出すことはありません。その代わりに、コントローラがマップされているURLを構築して呼び出します。ここでこれを行う方法の良い例です。 http://rstoyanchev.github.com/spring-31-and-mvc-test/#1

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(loader=WebContextLoader.class, locations = {"classpath:/META-INF/spring/applicationContext.xml", "classpath:/META-INF/spring/applicationContext-test-override.xml", "file:src/main/webapp/WEB-INF/spring/webmvc-config.xml"}) 
public class MyControllerTest { 
@Autowired 
WebApplicationContext wac; 
MockMvc mockMvc; 

@Before 
public void setup() { 
    this.mockMvc = MockMvcBuilders.webApplicationContextSetup(this.wac).build(); 
} 

@Test 
@Transactional 
public void testMyController() throws Exception { 
    this.mockMvc.perform(get("/mycontroller/add?param=1").accept(MediaType.TEXT_HTML)) 
    .andExpect(status().isOk()) 
    .andExpect(model().attribute("date_format", "M/d/yy h:mm a")) 
    .andExpect(model().attribute("myvalue", notNullValue())) 
    .andExpect(model().attribute("myvalue", hasSize(2))) 
    .andDo(print()); 
} 
} 

POM(春のマイルストーンのレポを使用する必要があります):

<!-- required for spring-test-mvc --> 
    <repository> 
     <id>spring-maven-milestone</id> 
     <name>Spring Maven Milestone Repository</name> 
     <url>http://maven.springframework.org/milestone</url> 
    </repository> 
... 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-test-mvc</artifactId> 
     <version>1.0.0.M1</version> 
     <scope>test</scope> 
    </dependency> 

注:春-MVC-テストlibには、生産準備ができていませんまだ。実装にはいくつかのギャップがあります。私は、Spring 3.2で完全に実装される予定です。

この方法は、コントローラを完全にテストするための素晴らしいアイデアです。コントローラーのマッピングが簡単にできないので、実際にはユニットテストが必要です。

2

バリデータは、呼び出されているコントローラメソッドの前に、メソッドパラメータに要求をバインドするプロセス中に呼び出されます。この場合、コントローラメソッドを直接呼び出しているので、バインディングと検証のステップはバイパスされています。

Spring MVCスタックを介してコントローラにコールすることです。これを行うにはいくつかの方法がありますが、私は最も良い方法はspring-test-mvcを使用して、スタックを呼び出します。テスト中

@Autowired 
private RequestMappingHandlerAdapter handlerAdapter; 

その後:

スタックをコールするもう一つの方法は、テストにHandlerAdapterにこの方法を注入することである

MockHttpServletRequest request = new MockHttpServletRequest("POST","/browser/create"); 
MockHttpServletResponse response = new MockHttpServletResponse(); 
httpRequest.addParameter(....);//whatever is required to create Browser.. 
ModelAndView modelAndView = handlerAdapter.handle(httpRequest, response, handler); 
2

基本的にあなたがして、this.controller = new MyController()でPOJOをインスタンス化そのメソッドthis.controller.add(...)と呼ばれます。ただの単純なJavaで、単純なオブジェクトで、コンテキストなし:@Validは考慮されません。

@ContextConfigurationは可能なカスタムバリデータなどを使用して、可能なBeanをロードするだけですが、@Validを処理するという魔法は行いません。

コントローラのaddメソッドへのリクエストをエミュレートする必要があります。それを完全にエミュレートし、検証が含まれています。 Springテスト機能を使用して(MockHttpServletRequestをインスタンス化するために)、あなたはそれほど遠かったわけではありません。

あなたは春の3.0.xのか以下を使用している場合、あなたはそれを動作させるために

new AnnotationMethodHandlerAdapter() 
     .handle(request, new MockHttpServletResponse(), this.controller); 

を行う必要があります。

Spring 3.1+を使用する場合、上記の解決策は機能しません(see this link for more info)!あなたはthis libraryを使用する必要があります(春のチームからは、心配しないでください)、次のSpringバージョンでの統合を待っています。 はその後、あなたはまた、これらの非常にinteresting slidesロッセンStoyanchevから見て

myMockController = MockMvcBuilders.standaloneSetup(new MyController()).build(); 
myMockController.perform(get("/browser/create")).andExpect(...); 

ような何かをする必要があります(私たちがここで話している部分がスライド#116から始まります)!

注:この種のテストが単体テストまたは統合テストと見なされるかどうかについては議論しません。リクエストの完全なパスをエミュレートするので、これは私たちがここでやっている統合テストであると言う人もいます。しかし、別の手では、Mockitoのような@Mockアノテーションを使ってコントローラをモックすることができます(あるいは、他のモックフレームワークと同様のことをすることができます)ので、テストの範囲をほぼ純粋な "ユニットテスト" 。もちろん、単純かつ古いJava +モックフレームワークでコントローラを単体テストでテストすることもできますが、この場合、@Valid検証をテストすることはできません。あなたの選択をしてください ! :)

関連する問題