입사 후 개발을 한지 벌써 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 테스트 방법에 대해 알아보자.
'web > Junit' 카테고리의 다른 글
Junit5 Test Container사용하여 테스트 환경 구축하기 (인프런 백기선님 강의 정리) (0) | 2019.12.26 |
---|---|
Spring Junit5 test Mockito (백기선님 인프런 강의) (0) | 2019.12.23 |
Spring BootJunit5 테스트 (백기선님 인프런 강의) (0) | 2019.12.23 |
Junit 정리 - 서비스 테스트 하기 (1) | 2018.05.27 |