Junit 정리 - MockMvc를 이용한 컨트롤러 테스트

web/Junit|2018. 5. 27. 00:42

입사 후 개발을 한지 벌써 3년 차가 되었다. 
개발을 처음 접할 때는 어떻게 구현해야 할지 어떻게 만들어야 하는지에 대한 관심이 더 컸다. 

하지만 요새는 개발 후 어떻게 테스트를 진행하여 먼저 버그를 예방(?)할 수 있는지 고민하기 시작했다.

성격이 덜렁거리거나 대충 하는 스타일은 아니었는데, 요즘 열정이 많이 식어서 그런지 단순한 부분에서 버그를 유발하는 것 같아서 TDD를 통해 고쳐보려고 Junit을 공부하기로 했다.

스프링의 각 영역인 Controller. Service, Dao에 대한 테스트를 총 3장에 걸쳐서 설명하겠다.

그리고 Mockito에 대한 설명을 추가로 진행하겠다.

0. 공통

우선 스프링에서 단위테스트를 진행하기 위해서는 몇가지 라이브러리가 필요하다. 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>
<!-- mockito프레임워크 사용을 위한 라이브러리 -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.9.5</version>
    <scope>test</scope>
</dependency>
<!-- spring test는 MockMvc와 다른 테스트를 포함한다. -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>3.2.3.RELEASE</version>
    <scope>test</scope>
</dependency>
cs



1. Controller
Controller 단위 테스트를 진행하기 위해서 MockMvc를 활용할 수 있다.

MockMvc란?
-> 브라우저에서 요청과 응답을 의미하는 객체로서 Controller 테스테 사용을 용이하게 해주는 라이브러리이다.

먼저 테스트를 진행할 컨트롤러는 다음과 같다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.wedul.wedulpos.user.controller;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
import com.wedul.wedulpos.user.dto.UserDto;
import com.wedul.wedulpos.user.service.UserService;
 
/**
 * User관련 컨트롤러
 * 
 * @author wedul
 *
 */
@RestController
@RequestMapping(value = "/user", method = RequestMethod.POST)
public class UserController {
    
    @Autowired
    UserService userService;
 
    /**
     * 로그인 요청
     * 
     * @param user
     * @return
     */
    @RequestMapping("/login")
    public boolean login(UserDto user) {
        return userService.login(user);    
    }
    
}
cs



그럼 이 컨트롤러 테스트를 위해 테스트 클래스를 만들어보자.


Annotation 설정

먼저 테스트 클래스를 만들기 위해서는 선 작업이 진행되어야 한다.

먼저 상위에 테스트 클래스에 대한 일부 설정값을 어노테이션으로 선언해야 한다.

SpringJUnit4ClassRunner.class
-> spring-test에서 제공하는 단위 테스트를 위한 클래스 러너

@ContextConfiguration
-> 테스트의 설정이 들어있는 xml의 위치

@Mock
-> 주입할 Service 객체를 Mock인스턴스로 선언

테스트를 진행하기전에 진행되야할 작업들은 @Before가 선언된 메서드에서 진행되어야 한다.
@Before 메서드에서는 다음과 같은 과정들이 선행되어야 한다.

@ContextConfiguration에서 함께 선언된 test를 위한 xml에는 테스트에서 사용될 bean들을 만들어주는 설정들이 들어있어야 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
// test-config.xml에 선언된 설정 내용
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <annotation-driven />
    
    <beans:bean id="userController" class="com.wedul.wedulpos.user.controller.UserController"></beans:bean>
    <beans:bean id="userService" class="com.wedul.wedulpos.user.serviceImpl.UserServiceImpl"></beans:bean>
</beans:beans>
cs



MockMvc 객체 생성

그럼 테스트를 진행할 Controller를 이용하여 MockMvc 객체를 생성한다. 테스트전에 선 작업이 진행하는 부분이기 때문에, @before 어노테이션이 선언된 메서드에서 작업을 진행한다.



1
2
3
4
5
6
7
8
9
@Autowired
UserController userController;
 
private MockMvc mockMvc;
 
@Before
public void setUp() throws Exception {
     mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
}
cs



MockMvc 객체를 이용해 테스트 진행

생성된 MockMvc 객체의 perform() 메소드를 이용하여, get, put, post, delete에 대한 요청을 진행할 수 있다.

또한 ContentType 설정 및 parameter 까지 자유롭게 생성하여 테스트에 사용할 수 있다.



1
2
3
4
5
@Test
public void testUserController() throws Exception {
    // login check
    mockMvc.perform(post("/user/login").param("id", "cjung"));
}
cs



테스트 요청 결과를 검증할 수 있는 방법을 ResultAction 인터페이스에서 제공하는 메서드들을 이용하여 검증할 수 있다.


// perform 요청에대한 예측결과를 검증한다.
ResultActions andExpect(ResultMatcher matcher) throws Exception;



1
2
3
4
5
6
7
8
9
10
11
12
13
mockMvc.perform(get("/person/1"))
    .andExpect(status().isOk())
    .andExpect(content().contentType(MediaType.APPLICATION_JSON))
    .andExpect(jsonPath("$.person.name").value("Jason"))
    .andExpect(redirectedUrl("/messages/123"));
 
 mockMvc.perform(post("/form"))
    .andExpect(status().isOk())
    .andExpect(redirectedUrl("/person/1"))
    .andExpect(model().size(1))
    .andExpect(model().attributeExists("person"))
    .andExpect(flash().attributeCount(1))
    .andExpect(flash().attribute("message", "success!"));
cs



// 결과에 대해 특정한 작업을 시행함. 파라미터로 전달하는 ResultHandler를 이용하여 다양하게 동작하게 설정가능
ResultActions andDo(ResultHandler handler) throws Exception;



1
mockMvc.perform(get("/form")).andDo(print());
cs




// 결과 자체를 전달받아, Reques, Response 등에 대한 정보를 반환받을 수 있음
MvcResult andReturn();


최종 완성 예제



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.wedul.wedulpos.user.test;
 
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
import java.nio.charset.Charset;
 
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 
import com.wedul.wedulpos.user.controller.UserController;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"test-confing.xml"})
public class UserControllerTest {
    
    @Autowired
    UserController userController;
    
    private MockMvc mockMvc;
    
    private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
            MediaType.APPLICATION_JSON.getSubtype(),
            Charset.forName("utf8"));
    
    @Before
    public void setUp() throws Exception {
         mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
    }
    
    @Test
    public void testUserController() throws Exception {
        // login check
        mockMvc.perform(post("/user/login").param("id", "cjung")).andExpect(status().isOk());
        
        // 비밀번호 찾기 확인
        System.out.println(mockMvc.perform(post("/user/password/find").param("id", "cjung")).andDo(print()));
        
        /* ObjectMapper mapper = new ObjectMapper();
         * this.mockMvc.perform(post(UserController.URL_USER_CREATE)
                .contentType(contentType)
                .content(mapper.writeValueAsString(new User("wedul"))))
                .andExpect(status().isOk())
                .andDo(print());*/
    }
 
}
cs





다음 시간에는 Service 테스트 방법에 대해 알아보자.

댓글()
  1. 지나가는 초보자 2019.02.23 22:10 댓글주소  수정/삭제  댓글쓰기

    정말 감사합니다.. 상세한설명에 초보자 눈높이에 맞춰져있어서 감동했습니다.

  2. july 2019.03.06 14:40 댓글주소  수정/삭제  댓글쓰기

    하루 반정도 헤매다가 다시 이 게시글로 돌아와서 해결했습니다ㅋㅋㅋㅋ
    감사합니다!!!!!

  3. 너무 감사합니다 2019.05.10 12:03 댓글주소  수정/삭제  댓글쓰기

    너무 잘 보고 갑니다 ㅠㅠ
    깔끔한 설명 너무 고맙습니다 ㅠㅠ