Spring - MVC TEST
요즘 학부시절 배운 테스트를 복습하고 있습니다.
생각해보니 CHANGOOS도 테스트를 작성해야겠다는 생각이 들었습니다.
좋은 개발자가 되기 위한 하나의 과정으로 테스트코드를 습관적으로 작성하라고 하셨는데.. 반성하게 됩니다..
<br/>
학부시절에 JUnit과 Mockito를 사용하여 테스트를 실습했었습니다.
<em>Mockito는 가상 객체를 말합니다. 테스트 하기위해서 가상의 객체가 필요할때 사용합니다.</em>
<em>A객체를 가상으로 만들어 x메소드를 호출할때 k값을 리턴해! 라고 설정하여 테스트에 필요한곳에 주입하여 사용합니다.</em>
<br/>
Web환경에서 URL요청을 어떻게 테스트해야하는지 생각이 떠오르지 않았습니다만..
역시나 Sping-MvcTest 라는 별도의 테스트 라이브러리가 존재하네요.
<br/>
<hr/>
다음과 같이 작성하였습니다.
<br/>
우선 pom.xml에 세가지 라이브러리를 추가했습니다. (Junit, Mockito, Spring-test)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
<scope>test</scope>
</dependency>
<br/>
그럼이제 하나의 예를 통해 구현을 진행해봅니다.
**src/main/java에 **패키지를 작성하고, 코드를 짰다면
**src/test/java에 **동일한 패키지를 작성하고, 테스트 클래스를 만듭니다.
<br/>
기존의 **IntroduceController **클래스는 다음과 같습니다.
@Controller
public class IntroduceController {
@Autowired
private CommonStringService commonStringService;
@RequestMapping(value = "/introduce")
public String introduce(Model model) {
String intro001 = commonStringService.get("INTRO", "001"); // 자기소개
String intro002 = commonStringService.get("INTRO", "002"); // 이력내용
model.addAttribute("intro001", intro001);
model.addAttribute("intro002", intro002);
return "introduce/introduce_view";
}
}
<br/>
그리고 다음과 같이 **IntroduceControllerTest **클래스를 만들었습니다.
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/appServlet/**-context.xml")
public class IntroduceControllerTest {
@Mock
CommonStringService commonStringService;
@InjectMocks
private IntroduceController introduceController;
private MockMvc mockMvc;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(introduceController).build();
}
}
다음은 기본 설정을 설명합니다. 타 테스트 클래스도 동일하게 작성하여 진행할 수 있을 것입니다.
-
** 1 : SpringJUnit4ClassRunner 클래스를 테스트 클래스로 사용하도록 합니다.
-
2 : 정의된 web.xml 설정이 아닌, 가상의 web.xml을 사용하도록 한다.
-
3 : 테스트를 위한 xml 설정파일을 지정합니다.
-
5 : Mock객체 설정합니다.
-
8 : 정의된 Mock 객체를 해당 객체에 주입하도록 한다.
-
11: URL을 호출하여 테스트 할 수있는 mockMvc 객체
-
13 : Test를 수행하기 전, 수행해야할 메소드
-
15 : Mockito 초기화
-
16 : mockMvc 초기화
<br/>
<br/>
<em>** @RunWith 어노테이션은 왜 사용하지?</em>
<em>@RunWith는 JUnit 프레임워크의 테스트 실행 방법을 확장할 때 사용하는 애노테이션이다. SpringJUnit4ClassRunner라는 JUnit용 테스트 컨텍스트 프레임워크 확장 클래스를 지정해주면 JUnit이 테스트를 진행하는 중에 테스트가 사용할 애플리케이션 컨텍스트를 만들고 관리하는 작업을 진행해준다.</em>
<em>출처 : <a href="https://countryxide.tistory.com/17">https://countryxide.tistory.com/17</a></em>
<br/>
<br/>
<br/>
그럼 이제 테스트 코드를 짜야합니다.
테스트코드는 3A (Arrange, Act, Assert) 동작으로 작동합니다.
Arrange는 테스트를 하기 위한 준비, Act는 실제 동작, Assert는 동작 후 비교. 다음과 같이 테스트코드를 구현하였습니다.
@Test
public void testIntroduce() throws Exception {
//Arrange
when(commonStringService.get("INTRO", "001")).thenReturn("자기소개");
when(commonStringService.get("INTRO", "002")).thenReturn("이력내용");
mockMvc.perform(get("/introduce")) // Act
.andExpect(status().isOk()) //Assert
.andExpect(view().name("introduce/introduce_view"))
.andExpect(model().attribute("intro001", "자기소개"))
.andExpect(model().attribute("intro002", "이력내용"));
}
-
3 : 해당메소드에 다음 매개변수가 들어가면, "자기소개"를 리턴합니다.
-
4 : 해당메소드에 다음 매개변수가 들어가면, "이력내용"을 리턴합니다.
-
6 : "/introduce" URL을 GET 메소드를 통해 호출한다.
-
7 : HTTP 상태값이 200인가?
-
8 : 뷰의 이름이 "introduce/introduce_view" 인가?
-
9 : Model의 "intro001" 값이 "자기소개"인가?
-
10 : Model의 "intro002" 값이 "이력내용"인가?
<br/>
<br/>
이 코드를 베이스로 URL 요청에 대한 Controller 테스트 코드를 작성 할 수 있습니다.
Service 클래스는 비즈니스로직이니 JUnit 테스트 코드로 작성하면 될 것입니다.
<br/>
<br/>
<hr/>
MockMvc 여러 테스트
HTTP 여러 요청에 대하여
mockMvc.perform(get("URL"))
mockMvc.perform(get("URL").param("p","01"))
mockMvc.perform(post("URL"))
mockMvc.perform(post("URL").param("p","01"))
mockMvc.perform(fileUpload("URL"),file("name", byte[]))
<br/>
-
1 : GET 메소드 호출
-
2 : 파라미터와 함께 GET 메소드 호출
-
3 : POST 메소드 호출
-
4 : 파라미터와 함께 POST 메소드 호출
-
5 : multipart 호출, 파일 포함
<br/>
<br/>
요청URL이 REDIRECT로 리턴되어지는 경우
mockMvc.perform(get("URL"))
.andExpect(redirectedUrl("REDIRECT URL"))
.andExpect(status().isFound());
<br/>
요청 URL이 RESPONSE BODY인 경우
mockMvc.perform(get("URL"))
.andExpect(status().isOk())
.andExpect(content().json("JSON STRING"));
이 경우에는 JSON을 비교해주는 Jsonassert 라이브러리가 필요합니다.
<br/>