Spring FrameworkのUnitテスト実装方法 3-2.Controllerテスト(Junit4, spring-test, MockMvc(StandaloneSetupMode), mockito)

Spring FrameworkのUnitテスト実装方法 3-2.Controllerテスト(Junit4, spring-test, MockMvc(StandaloneSetupMode), mockito)

Spring FrameworkのUnitテスト実装方法 3-2.Controllerテスト(Junit4, spring-test, MockMvc(StandaloneSetupMode), mockito)

【サンプルソース】
TERASOLUNA Server Framework for Java (5.x) Development Guideline
サンプルソースはこちら
(これのMyBatis3を使用したパターンで作成してます。)

3-2.Controllerテスト Junit4, spring-testライブラリ、mockitoによるモック化を使用したパターン

controllerクラスのテストです。
Serviceクラスのメソッドをmockitoライブラリを用いてモック化しています。
今回も、MockMVCはStandaloneSetupのパターンで実装します。

【pom.xml】

Junit、spring-test、mockitoライブラリが必要です。

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

【テスト対象クラス】

TodoController.java
テスト対象はfinishメソッド

@Controller
@RequestMapping("todo")
public class TodoController {

    @Inject
    TodoService todoService;
    
    @Inject
    Mapper beanMapper;
    
    @ModelAttribute
    public TodoForm setUpForm() {
        TodoForm form = new TodoForm();
        return form;
    }
    
    @RequestMapping(value = "finish", method = RequestMethod.POST)
    public String finish(@Validated({Default.class}) TodoForm form, BindingResult bindingResult, Model model, RedirectAttributes attributes) {
        if(bindingResult.hasErrors()) {
            return list(model);
        }
        
        try {
            todoService.finish(form.getTodoId());
        }catch (BusinessException e) {
            // TODO: handle exception
            model.addAttribute(e.getResultMessages());
            return list(model);
        }
        
        attributes.addFlashAttribute(ResultMessages.success().add(ResultMessage.fromText("Finished successfully!")));
        return "redirect:/todo/list";
    }
}

(対象メソッドの処理の部分だけ抜粋してます。)

【テストクラス】

TodoControllerTestVerStandaloneMockito.java

public class TodoControllerTestVerStandaloneMockito {

    @Rule
    public MockitoRule mockito = MockitoJUnit.rule();
    
    @InjectMocks
    TodoController target;
    
    @Mock
    TodoService todoService;
    
    MockMvc mockMvc;
    
    @Before
    public void setUp() {
        //standaloneモードでmockMvcを起動
        mockMvc = MockMvcBuilders.standaloneSetup(target).build();
    }
    
    //正常動作のパターン
    @Test
    public void testFinish() throws Exception {
        
        //mockを作成(getTodoTrueDataメソッドでモック用のデータを返している)
        when(todoService.finish(anyString())).thenReturn(getTodoTrueData());
        
        //Controllerに投げるリクエストを作成
        MockHttpServletRequestBuilder getRequest = 
                MockMvcRequestBuilders.post("/todo/finish")
                                        .param("todoId", "cceae402-c5b1-440f-bae2-7bee19dc17fb")
                                        .param("todoTitle", "one");
        
        //Controllerにリクエストを投げる(リクエストからfinishメソッドを起動させる。)
        ResultActions results = mockMvc.perform(getRequest);
        
        //結果検証
        results.andDo(print());
        results.andExpect(status().isFound());
        results.andExpect(view().name("redirect:/todo/list"));
        
        //FlashMapのデータを取得(model.addFlashAttributeに設定したmessageオブジェクトのtextを取得する。半ば強引)
        FlashMap flashMap = results.andReturn().getFlashMap();
        Collection<Object> collection = flashMap.values();
        for(Object obj : collection) {
            ResultMessages messages = (ResultMessages) obj;
            ResultMessage message = messages.getList().get(0);
            String text = message.getText();
            assertEquals("Finished successfully!", text);
        }
    }
    
    //serviceメソッドが異常を返した場合
    @Test
    public void testFinishThrowException() throws Exception {
        
        //mockを作成(exceptionを返す)
        ResultMessages messages = ResultMessages.error();
        messages.add(ResultMessage.fromText("[E004]The requested Todo is not found. (id=cceae402-c5b1-440f-bae2-7bee19dc17fb)"));
        
        //mockを作成
        when(todoService.finish(anyString())).thenThrow(new BusinessException(messages));
        
        //Controllerに投げるリクエストを作成
        MockHttpServletRequestBuilder getRequest = 
                MockMvcRequestBuilders.post("/todo/finish")
                                        .param("todoId", "cceae402-c5b1-440f-bae2-7bee19dc17fb")
                                        .param("todoTitle", "one");
        
        //Controllerにリクエストを投げる(リクエストからfinishメソッドを起動させる。)
        ResultActions results = mockMvc.perform(getRequest);
        
        //結果検証
        results.andDo(print());
        results.andExpect(status().isOk());
        results.andExpect(view().name("todo/list"));
        
        //model Confirmation
        ModelAndView mav = results.andReturn().getModelAndView();
        
        //結果検証
        ResultMessages actResultMessages = (ResultMessages)mav.getModel().get("resultMessages");
        ResultMessage actResultMessage = actResultMessages.getList().get(0);
        String text = actResultMessage.getText();
        assertEquals("[E004]The requested Todo is not found. (id=cceae402-c5b1-440f-bae2-7bee19dc17fb)", text);
    }
}

内容は3-1の実装内容と変わってません。
ポイントとしては、mockitoライブラリのwhen()メソッドで正常、異常パターンで返してる時の形式が違うということくらいですかね。
thenThrow()メソッドでExceptionを返すことができます。

【その他設定】

特になし

やっぱりmockitoのいいところはテストクラス上でモックの設定もできるということですね~
モッククラスをわざわざ作らなくてもよくなりますし。テストクラスのパターンが増えていくつもファイルが出来たときも管理が楽そうです。

サンプルソースはgithubで公開してます。
Spring Unit Test pattern12

Controller Testカテゴリの最新記事