Spring Test
-
Upload
khangminh22 -
Category
Documents
-
view
3 -
download
0
Transcript of Spring Test
1. 도구 개요
도구명 Spring Test (http://spring.io)
라이선스 Apache License v2.0
소개 • 스프링 프레임워크에서 제공하는 테스트 지원 도구
• 스프링에 대한 통합 테스트 및 스프링 MVC 통합 테스트 지원
특징 • 통합 테스트를 위한 스프링 컨테이너 생성, 트랜잭션, DB 연동 관리 지원
• 스프링 MVC 통합 테스트 지원
주요기능
• 애노테이션을 통한 컨테이너 초기화, 테스트 메서드에 대한 트랜잭션 관리 지원
• 테스트를 위한 DB 초기화 지원
• MockMVC를 통한 스프링 MVC 컨트롤러 통합 테스트 지원
• 웹 관련 Mock 테스트를 위한 Mock 객체 지원
실행환경 • JUnit 등 테스트 프레임워크 사전설치도구 • JDK 5+ (Spring 3.x)
• JDK 6+ (Spring 4.x)
카테고리 • 테스트 최신버전 • 4.1 (2014.10)
관련도구 • 자바 IDE(Eclipse, IntelliJ, NetBeans 등)
2
1.1 도구 정보 요약
1. 도구 개요
3
1.2 스크린 갭쳐 및 주요 기능
• Spring 애플리케이션 및 SpringMVC 웹 애플리케이션 단위 테스트
• 다양한 애노테이션 지원으로 테스팅 작업 생산성 향상
• 트랜잭션 지원, HTTP요청, HTTP응답, 모델, JSON/XML에 대한 테스트 지원
2. 설치 및 준비
2.1 메이븐 의존 설정 (1/2)
• 스프링 버전에 맞게 spring-test 의존과 junit 의존을 추가한다.
– 테스트로 사용되므로, scope를 test로 지정
– 테스트 코드 작성의 편리함을 위해 hamcrest-library 추가
5
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <!-- 사용하는 버전에 맞게 수정 --> <version>4.1.1.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <!-- 스프링 버전 별로 junit 버전 선택 --> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-library</artifactId> <version>1.3</version> <scope>test</scope> </dependency>
스프링 버전 JUnit/hamcrest 버전
4.1.x 4.11 / 1.3
4.0.x 4.11 / 1.3
3.2.x 4.11 / 1.3
3.1.x 4.9 / 1.1
2. 설치 및 준비
2.1 메이븐 의존 설정 (2/2)
• 설치 확인
6
추가된 의존 모듈 - spring-test - junit - hamcrest-core - hamcrest-library
2. 설치 및 준비
2.2 이클립스 Favorites 설정
• 이클립스의 static import 지원 기능을 위해 Favorites에 두 클래스 추가
– org.junit.Assert / org.hamcrest.Matchers
• 등록 방법
– Window > Preferences > Java/Editor/Content Assist/Favorites
– New Type을 이용해서 클래스 등록(org.junit.Assert, org.hamcrest.Matcher)
7
Ctrl+1 키로 static 멤버에 대한 static import 코드 지원
3.1 스프링 통합 테스트: 통합 테스트 기본 코드 (1/3)
9
3. 주요기능
import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ContextConfiguration("classpath:simple.xml") @RunWith(SpringJUnit4ClassRunner.class) public class HelloTest { @Autowired private Hello hello; @Test public void test() { assertThat(hello.hello("spring"), equalTo("Hello, spring")); } }
스프링 설정 목록
스프링의 JUnitRunner를 이용해서 테스트 실행
테스트 할 스프링 빈 객체를 주입받음
<!-- simple.xml --> <bean id="hello" class="sp.Hello"> </bean>
3.1 스프링 통합 테스트: 통합 테스트 기본 코드 (2/3)
10
3. 주요기능
• 스프링 테스트 지원 기능 사용 전/후
public class HelloTest { private static AbstractApplicationContext ctx; @BeforeClass public static void init() { ctx = new GenericXmlApplicationContext( "simple.xml"); } @AfterClass public static void close() { ctx.close(); } @Test public void test() { Hello hello = ctx.getBean(Hello.class); assertThat(hello.hello("spring"), equalTo("Hello, spring")); } @Test … }
@ContextConfiguration("classpath:simple.xml") @RunWith(SpringJUnit4ClassRunner.class) public class HelloTest { @Autowired private Hello hello; @Test public void test() { assertThat(hello.hello("spring"), equalTo("Hello, spring")); } @Test … }
컨텍스트 생성과 관련된 부분이 간결해짐
3.1 스프링 통합 테스트: 통합 테스트 기본 코드 (3/3)
11
3. 주요기능
• @ContextConfiguration : 사용할 스프링 설정 지정
– XML 설정인 경우: value나 locations 속성 사용
– 자바 설정인 경우: classes 속성 사용 • @RunWith(SpringJUnit4ClassRunner.class)
– 스프링이 제공하는 JUnitRunner를 이용해서 테스트 실행
– @ContextConfiguration의 설정 정보 이용해서 스프링 컨텍스트 생성
– 테스트 클래스의 @Autowired, @Resource 등의 필드에 자동 주입 처리
– 테스트 메서드 마다 컨텍스트를 생성하지 않도록 컨텍스트를 캐싱
// 2개 이상 XML @ContextConfiguration({"classpath:service.xml", "classpath:dao.xml"}) // 2개 이상 Java 설정 @ContextConfiguration(classes = {ServiceConf.class, DaoConf.class} )
@RunWith(SpringJUnit4ClassRunner.class) public class HelloTest { @Autowired private Hello hello;
3.2 스프링 통합 테스트: 트랜잭션 처리 (1/3)
12
3. 주요기능
• DB 연동 기능 테스트시, 테스트 메서드마다 트랜잭션 롤백 처리 가능
• 적용 방법 – @TransactionConfiguration 테스트 클래스 – @Transactional 테스트 클래스 또는 테스트 메서드
* 테스트 클래스에 적용 : 전체 테스트 메서드를 트랜잭션 범위로 실행 * 테스트 메서드에 적용 : 해당 메서드를 트랜잭션 범위로 실행
• @TransactionalConfiguration
– 스프링 트랜잭션 관리자 이용해서 트랜잭션 처리하도록 설정 – 두 개 이상의 트랜잭션 관리자 존재시,
* 이름이 transactionManager인 빈을 사용 * 해당 이름의 빈이 존재하지 않으면, transactionManager 속성을 이용해서
트랜잭션 관리자 지정
• 트랜잭션 적용 범위
– @Test 메서드와 @Before/@After 메서드가 한 트랜잭션에 실행 * 즉, [@Before 메서드 @Test 메서드 @After메서드]가 한 범위
3.2 스프링 통합 테스트: 트랜잭션 처리 (2/3)
13
3. 주요기능
• 테스트 메서드 트랜잭션의 롤백 설정 방법
– @TransactionConfiguration의 defaultRollback 설정 * 테스트 클래스 전체에 적용 * 기본값은 true
– @Rollback을 이용한 명시적 롤백 방식 지정 * 개별 테스트 메서드에 적용 * 값을 true로 지정하면 테스트 실행 후 트랜잭션 롤백
3.2 스프링 통합 테스트: 트랜잭션 처리 예시(3/3)
14
3. 주요기능
import static org.hamcrest.Matchers.greaterThan; …생략 import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:/applicationContext.xml") @TransactionConfiguration public class MemberIntTest { @Autowired private MemberRegisterService memRegSvc; @Autowired private MemberDao memberDao; @Test public void counts() { assertThat(memberDao.counts(), equalTo(10)); } @Transactional @Test public void register() { RegisterRequest regReq = new RegisterRequest(); … Long newMemberId = memRegSvc.register(regReq); assertThat(newMemberId, greaterThan(0)); } }
테스트 메서드를 스프링의 트랜잭션 범위에서 실행하고, 트랜잭션 롤백
3. 주요기능
3.3 스프링 MVC 테스트: 소개
15
• 스프링 MVC 컨트롤러에 대한 단위/통합 테스트 코드 실행 지원
– 스프링 3.2 버전부터 포함
• 주요 특징
– DispatcherServlet을 통해 실행하는 것과 (거의) 동일한 테스트 환경
* WAS를 실행하지 않고, 컨트롤러에 대한 통합 테스트 가능
– 스프링 컨텍스트 또는 개별 컨트롤러를 이용한 테스트 실행 지원
– 다양한 요청 설정 지원
* URL, 파라미터, HTTP 메서드(GET, POST 등)
* 요청 몸체 데이터 (쿼리문자열, JSON, XML 등)
* 쿠키, 세션 설정
– 응답 결과에 대한 검증 지원
* 응답 상태 코드, 컨텐츠 타입
* 뷰 이름, 응답 모델, 쿠키, 세션 등
* JSON/XML 응답에 대한 검증 지원
3. 주요기능
3.3 스프링 MVC 테스트: 기본 코드
16
// 아래 코드에서 o...는 org.springframework. 를 생략한 것 import static o...test.web.servlet.request.MockMvcRequestBuilders.*; import static o...test.web.servlet.result.MockMvcResultMatchers.*; import o...test.context.web.WebAppConfiguration; import o...test.web.servlet.MockMvc; import o...test.web.servlet.result.MockMvcResultHandlers; import o...test.web.servlet.setup.MockMvcBuilders; import o...web.context.WebApplicationContext; …생략 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:spring-*.xml"}) @WebAppConfiguration public class HelloControllerTest { @Autowired private WebApplicationContext ctx; private MockMvc mockMvc; @Before public void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build(); } @Test public void testHello() throws Exception { mockMvc.perform(get("/hello").param("name", "spring")) .andExpect(status().isOk()) .andExpect(view().name("hello")) .andExpect(model().attributeExists("greeting")); }
WebApplicationContext 생성 설정
MockMvc는 DispatcherServlet과
동일하게 동작
MockMvc 생성
스프링 MVC 설정 포함
웹 요청 생성 및 실행
스프링 MVC 응답 검증
3. 주요기능
3.3 스프링 MVC 테스트: MockMvc 생성 (1/2)
17
• WebApplicationContext를 이용한 생성
– 통합 테스트 목적
@ContextConfiguration({"classpath:spring-*.xml"}) @WebAppConfiguration public class HelloControllerTest { @Autowired private WebApplicationContext ctx; private MockMvc mockMvc; @Before public void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build(); }
WebApplicationContext 생성 필요
3. 주요기능
3.3 스프링 MVC 테스트: MockMvc 생성 (2/2)
18
• 단일 컨트롤러를 이용한 생성
– 단일 컨트롤러에 대한 단위 테스트 목적
public class HelloControllerTest { private MockMvc mockMvc; @Before public void setUp() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/view/"); viewResolver.setSuffix(".jsp"); mockMvc = MockMvcBuilders.standaloneSetup(new HelloController()) .setViewResolvers(viewResolver).build(); }
3. 주요기능
3.3 스프링 MVC 테스트: GET/POST 요청 설정 (1/2)
19
• MockMvc의 perform() 메서드를 이용해서 요청 생성
– MockMvcRequestBuilders의 static 메서드를 이용해서 요청 생성
– 코드 가독성을 위해 static import 사용
• GET 요청 생성/POST 요청 생성
– MockMvcRequestBuilders.get() 또는 post() 메서드 사용
* put(), delete() 메서드도 지원
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@Test public void testHello() throws Exception { mockMvc.perform(get("/hello").param("name", "spring")) .andExpect(status().isOk()) .andExpect(model().attributeExists("greeting")); }
3. 주요기능
3.3 스프링 MVC 테스트: GET/POST 요청 설정 (2/2)
20
• MockHttpServletRequestBuilder
– get(), post() 등 메서드는 MockHttpServletRequestBuilder를 리턴
• MockHttpServletRequestBuilder의 주요 메서드
– param(String name, String… values)
– cookie(Cookie… cookies)
– headers(String name, Object… values)
– sessionAttr(String name, Object value)
– contentType(MediaType mediaType)
– content(String content)
– contextPath(String contextPath)
3. 주요기능
3.3 스프링 MVC 테스트: 응답 검증 (1/3)
21
• perform() 메서드가 리턴하는 ResultActions의 andExpect()로 검증
– andExpect()의 검증 코드는 MockMvcResultMatchers 이용 생성
– 코드 가독성 위해 MockMvcResultMatchers를 static import 처리
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; … @Test public void testHello() throws Exception { mockMvc.perform(get("/hello").param("name", "spring")) .andExpect(status().isOk()) .andExpect(model().attributeExists("greeting")); }
3. 주요기능
3.3 스프링 MVC 테스트: 응답 검증 (2/3)
22
• 주요 검증 코드
– status().isOk() : 응답 상태 코드가 200인지 여부
* isForbidden() : 403 * isNotFound() : 404 * isInternalServerError() : 500 * is2xxSuccessful() * is3xxRedirection() * is4xxClientError() * is5xxServerError() * is(int status) : 응답 코드가 status인지
– view().name("hello") : 뷰 이름이 "hello"인지 여부
– model().attribute("uac", value) : "uac" 모델 값이 value인지 여부
* attributeExists("greeting") : "greeting" 모델이 존재하는지 여부 * attributeHasErrors(String... names) : 지정한 모델이 에러를 가져야 함 * attributeHasNoErrors(String... names) : 지정한 모델이 에러를 갖지 말아야
함 * hasErrors() : 에러를 가져야 함 * hasNoErrors() : 에러가 없어야 함
3. 주요기능
3.3 스프링 MVC 테스트: 응답 검증 (3/3)
23
• 주요 검증 코드
– redirectedUrl("/") : 리다이렉트 경로가 "/"인지 여부 (컨텍스트 경로 기준)
– cookie().value("ac", "a12l4") : 응답의 "ac" 쿠키 값이 "a12l4"인지 여부
* exists("ac") : 응답에 "ac" 쿠키가 존재하는지 여부 * doesNotExist("ac") : 응답에 "ac" 쿠키가 존재하지 않는지 여부 * maxAge("name", 100) : "name" 쿠키의 유효시간이 100인지 여부 * path("name", "/") : "name" 쿠키의 경로가 "/"인지 여부
– header().string("name", "value") : "name" 헤더 값이 "value"인지 여부
* longValue("name", 1L) : "name" 헤더 값이 1L인지 여부 * doesNotExist("ac") : 헤더가 존재하지 않는지 여부
3. 주요기능
3.3 스프링 MVC 테스트: MvcResult 이용
24
• MvcResult
– MockMvc에서 스프링 MVC를 실행하는데 사용된 객체 구할 때 사용
• MvcResult의 주요 메서드
– ModelAndView getModelAndView()
* 요청을 통해 생성된 ModelAndView 객체를 구함
– MockHttpServletRequest getRequest()
* 컨트롤러를 실행할 때 생성한 요청 객체를 구함
* 세션에 직접 접근할 때 사용
– MockHttpServletResponse getResponse()
* 큰트롤러의 실행 결과로 생성된 응답 객체를 구함
MvcResult mvcResult = mockMvc.perform(get("/hello").param("name", "spring")) .andExpect(status().isOk()) .andExpect(model().attributeExists("greeting")) .andReturn();
3. 주요기능
3.3 스프링 MVC 테스트: JSON 응답 검증
25
• JSON 응답 결과 검증을 위해 필요한 의존 추가
• 전형적인 응답 검증 코드
• 주요 검증 코드
– jsonPath(path).value(값)
– jsonPath(path).value(Matcher)
– jsonPath(path).exists()
– jsonPath(path).doesNotExist()
• jsonPath()의 경로 값 : http://goessner.net/articles/JsonPath/ 문서 참고
<dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> <version>0.9.0</version> <scope>test</scope> </dependency>
mockMvc.perform(get("/books.json")) .andExpect( jsonPath("$.books[2].title").value("제목3") );
3. 주요기능
3.3 스프링 MVC 테스트: XML 응답 검증
26
• 전형적인 XML 응답 검증 코드
• 주요 검증 코드
xpath(path).string(값)
xpath(path).number(값)
xpath(path).booleanValue(값)
xpath(path).exists()
xpath(path).doesNotExist()
mockMvc.perform(get("/books.xml")) .andExpect(xpath("/book-list/book[3]/title").string("제목3")) .andExpect(xpath("/book-list/book[3]/%s", "price").number(3000.0));
3. 주요기능
3.3 스프링 MVC 테스트: 기타 설정
27
• 서블릿 필터
– addFilter() 메서드 사용
@Before public void init() { DelegatingFilterProxy securityFilter = new DelegatingFilterProxy(); securityFilter.setTargetBeanName("springSecurityFilterChain"); securityFilter.setServletContext(context.getServletContext()); mockMvc = MockMvcBuilders .webAppContextSetup(context) .addFilter(securityFilter, "/*") .build(); }
3. 주요기능
3.4 테스트 DB 초기화: DB 데이터 초기화의 필요성 (1/2)
28
• DB 기반 통합 테스트가 실패할 가능성
– 기능 테스트, 다른 통합 테스트 등을 통해 DB의 데이터 변경 발생
– 예, 웹으로 회원 가입 테스트시, 아래 테스트 코드는 실패할 수 있음
@ContextConfiguration("classpath:spring-*.xml") @RunWith(SpringJUnit4ClassRunner.class) public class HelloTest { @Autowired private MemberRegisterService memRegSvc; @Test public void registerSuccess() { RegistRequest regReq = new RegistRequest(); regReq.setId("user0"); Long id = memRegSvc.regist(regReq); … } @Test public void duplicateId() { RegistRequest regReq = new RegistRequest(); regReq.setId("user1"); try { memRegSvc.regist(regReq); fail("익셉션 발생해야 함"); } catch(DuplicateIdException ex) { … } } }
웹 기능 테스트에서 이 ID를 이용해서 가입 기능을 테스트 했다면?
테스트 실행 전에 해당 ID를 가진 데이터가 존재하지 않는다면?
3. 주요기능
3.4 테스트 DB 초기화: DB 데이터 초기화의 필요성 (2/2)
29
• 테스트 실행 전 DB 데이터 초기화
– 매번 동일한 데이터를 이용해서 테스트가 실행되도록 함
– DB 데이터 변경으로 인해 발생하는 테스트 실패를 방지
• 주요 도구
– spring-jdbc의 ResourceDatabasePopulator 사용
* 스프링 3.0 버전부터 지원
– spring-test의 @Sql 애노테이션
* 스프링 4.1 버전부터 지원
3. 주요기능
3.4 테스트 DB 초기화: ResourceDatabasePopulator 이용 (1/2)
30
• SQL 스크립트를 실행하는 기능 제공
• 테스트 코드에서의 활용
– @Before 메서드에서 테이블 삭제·생성, 초기 데이터 삽입
– @After 메서드에서 변경된 데이터 원복
•사용 방법 1: 스프링 통합 테스트
– 컨텍스트로부터 DataSource 가져와 실행
import javax.sql.DataSource; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; @ContextConfiguration("classpath:applicationContext.xml") @RunWith(SpringJUnit4ClassRunner.class) public class ShopIntTest { @Autowired private DataSource dataSource; @Before public void setUp() { ResourceDatabasePopulator dbPop = new ResourceDatabasePopulator(); dbPop.addScripts(new ClassPathResource("shop.ddl.sql"), new ClassPathResource("shop.init.sql")); dbPop.execute(dataSource); // DataSource 전달은 스프링 4.1부터 지원 } @Test public void someTestCodeForDataBaseIntegration() throws Exception { …테스트 코드 실행 } }
3. 주요기능
3.4 테스트 DB 초기화: ResourceDatabasePopulator 이용 (2/2)
31
• 사용 방법 2: Connection 제공
import javax.sql.DataSource; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; public class ShopIntTest { @Before public void setUp() { Connection conn = getConnection(); // Connection을 생성 try { ResourceDatabasePopulator dbPop = new ResourceDatabasePopulator(); dbPop.addScripts(new ClassPathResource("shop.ddl.sql"), new ClassPathResource("shop.init.sql")); dbPop.execute(conn); } finally { try { conn.close(); } catch(SQLException ex) {} } } @Test public void someTestCodeForDataBaseIntegration() { …테스트 코드 실행 } }
3. 주요기능
3.4 테스트 DB 초기화: @Sql 애노테이션 이용 (1/5)
32
• @Sql 애노테이션
– spring-test의 쿼리 실행 기능으로 4.1 버전부터 지원
• @Sql 애노테이션의 기본 사용법
import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ContextConfiguration("classpath:spring-*.xml") @RunWith(SpringJUnit4ClassRunner.class) @Sql({"classpath:shop.ddl.sql", "classpath:shop.init2.sql"}) public class ShopIntTest { @Autowired private ShopService shopService; @Test public void list() { … } @Test public void insert() { … } }
각 테스트 메서드 실행 전에 쿼리 실행
3. 주요기능
3.4 테스트 DB 초기화: @Sql 애노테이션 이용 (2/5)
33
• @Sql 애노테이션 테스트 메서드 적용
– 테스트 메서드에 적용시, 클래스에 적용된 @Sql 애노테이션 무시
import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ContextConfiguration("classpath:spring-*.xml") @RunWith(SpringJUnit4ClassRunner.class) @Sql({"classpath:shop.ddl.sql", "classpath:shop.init2.sql"}) public class ShopIntTest { @Autowired private ShopService shopService; @Test public void list() { … } @Sql("classpath:shop.init3.sql") @Test public void insert() { … } }
list() 테스트 메서드 실행시 클래스의 @Sql 사용
insert() 테스트 메서드 실행시 자신의 @Sql 사용
3. 주요기능
3.4 테스트 DB 초기화: @Sql 애노테이션 이용 (3/5)
34
• @Sql 애노테이션 설정
– 파일 경로 설정
* value 속성: @Sql("a.sql"), @Sql({"a.sql, b.sql"}) * scripts 속성: @Sql(scripts="a.sql"), @Sql(scripts={"a.sql, b.sql"})
– 실행 시점 설정
* executionPhase 속성 사용 : @Sql(scripts="…", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD) * ExecutionPhase 열거타입의 값 : BEFORE_TEST_METHOD, AFTER_TEST_METHOD
• 두 개 이상의 @Sql 애노테이션
– 자바8: 연속된 @Sql 애노테이션 사용
– 자바7 이하: @SqlGroup 사용
@Sql("init.sql") @Sql(scripts="clear.sql", executionPhase=ExecutionPhase.AFTER_TEST_METHOD) @Test public void someTest() { … }
@SqlGroup( { @Sql("init.sql"), @Sql(scripts="clear.sql", executionPhase=ExecutionPhase.AFTER_TEST_METHOD)} ) @Test public void someTest() { … }
3. 주요기능
3.4 테스트 DB 초기화: @Sql 애노테이션 이용 (4/5)
35
• @SqlConfig를 이용한 설정
– @Sql이 사용할 DataSource, TransactionManager, 인코딩 등 설정
– 예 : @Sql(scripts=…, config=@SqlConfig(encoding="utf-8"))
• @SqlConfig의 주요 속성
속성 설명
dataSource • 쿼리를 실행할 때 사용할 DataSource 빈 이름 설정 • 다음의 경우 dataSource 속성의 값을 지정하지 않아도 됨
– DataSource가 1개인 경우 – 트랜잭션관리자의 getDataSource()로 구할 수 있는 경우 – 이름이 "dataSource"인 DataSource가 존재할 경우 – 클래스 범위 @Sql에서 설정한 경우
• 예: @Sql(…, config=@SqlConfig(dataSource="xsDs"))
transactionManager • 트랜잭션 관리자의 빈 이름 지정 • 다음의 경우 지정하지 않아도 됨
– 트랜잭션관리자 1개인 경우 – 이름이 "transactionManager"인 트랜잭션 관리자가 존재할 경우 – 클래스 범위 @Sql에서 설정한 경우
encoding • 쿼리 파일의 인코딩 (기본값은 플랫폼의 인코딩)
commentPrefix • 주석의 시작 문자 지정 (지정하지 않을 경우 "--"를 주석으로 유추
transactionMode • 트랜잭션 범위 지정
3. 주요기능
3.4 테스트 DB 초기화: @Sql 애노테이션 이용 (5/5)
36
• @SqlConfig의 transactionMode 값
– SqlConfig.TransactionMode에 정의
– TransactionMode 열거 타입 값
값 설명
DEFAULT • 클래스에 적용된 경우 INFERRED로 동작한다. • (클래스에 없고) 메서드에만 적용된 경우 INFERRED로 동작한다. • (클래스에 설정이 있고) 메서드에 적용된 경우 클래스의 설정을 따
른다.
INFERRED • 다음 규칙에 따라 SQL 스크립트를 실행한다. – 트랜잭션 관리자와 DataSource 모두 없으면 익셉션 발생 – DataSource만 존재하면, 해당 DataSource를 이용해서 스크립트
실행 – 트랜잭션 관리자가 존재하고 DataSource가 없으면, 트랜잭션 관
리자로부터 DataSource를 구해서 스크립트 실행 – 트랜잭션 관리자와 DataSource가 모두 존재할 경우, 트랜잭션이
존재하면 트랜잭션 안에서 스크립트를 실행하고 트랜잭션이 존재하지 않으면 새로운 트랜잭션을 생성해서 스크립트를 실행한다.
ISOLATED • SQL 스크립트를 별도 트랜잭션으로 실행한다. 트랜잭션 관리자와 DataSource를 설정해야 한다.
4. 활용 예제
4.1 예제 소개
38
• 스프링 테스트의 실제 적용 코드를 살펴보기 위한 웹 어플리케이션 예제
• DB 초기화 및 회원 가입 Bean 테스트 – 스프링 통합테스트를 위한 DB초기화 및 @Sql을 이용한 데이터 초기화
• MockMvc를 이용한 HTTP GET/POST 처리 테스트
– GET/POST/파라미터 전송/응답 뷰 검증
• MockMvc를 이용한 HTTP Session 처리 테스트 – 로그인 성공 후, 세션 생성 및 리다이렉션 처리 검증
• MockMvc를 이용한 HTTP Cookie 처리 테스트
– 로그인 성공/로그아웃시 쿠키 생성/삭제 처리 검증
• MockMvc를 이용한 JSON 요청/응답 처리 테스트 – REST로 요청하고 JSON을 전송/응답하는 처리 검증
4. 활용 예제
4.2 예제 구성 (1/3)
39
• 테스트 코드를 실행해보기 위한 간단한 예제
– 코드 구성
영역 코드 설명
모델 Member.java 모델 클래스
DAO MemberDao.java, JdbcMemberDao.java MEMBER를 위한 DAO
서비스 MemberRegisterService.java, MemberRegisterServiceImpl.java RegisterRequest.java
회원 가입 처리 요청 확인 정보
AuthService.java, AuthServiceImpl.java, AuthRequest.java 인증 처리
컨트롤러 MemberRegisterController.java, RegisterRequestValidator.java MemberRestAPI.java
회원 가입 웹 요청 처리
LoginController.java, AuthRequestValidator.java Login2Controller.java, Logout2Controller.java
로그인/로그아웃 웹 요청 처리
뷰 main.jsp 첫 화면
loginForm.jsp 로그인 폼
registerForm.jsp / registered.jsp 회원 가입 폼, 결과 화면
스프링 설정 spring.xml 스프링 설정
서블릿 설정 web.xml 서블릿 설정
테스트 initdata.sql 데이터 초기화 쿼리
4. 활용 예제
4.2 예제 구성 (2/3)
40
• 사전 준비
– 이클립스 메이븐 플러그인 설치
* Luna(4.4), Kepler(4.3)의 JEE 버전은 설치되어 있음
– 자바 7 버전
– MySQL DB/계정/테이블 생성
• 코드 다운로드:
– https://github.com/madvirus/sptest 방문
– 사이트 우측의 'Download ZIP' 아이콘 클릭해서 소스 다운로드
– 원하는 곳에 압축 해제
create user 'spring4'@'localhost' identified by 'spring4'; create database sample character set=utf8; grant all privileges on sample.* to 'spring4'@'localhost'; create table sample.MEMBER ( ID varchar(100) primary key, PASSWORD varchar(100), NAME varchar(100) ) engine=InnoDB character set = utf8;
4. 활용 예제
4.2 예제 구성 (3/3)
41
• 이클립스에서 메이븐 프로젝트 임포트
– File → Import → Maven/Existing Maven Projects
– Root Directory로 압축 푼 디렉토리 선택 후 [Finish]
4. 활용 예제
4.3 스프링 통합 테스트: DB 초기화 예제 (1/2)
42
• @Sql로 지정할 sql 파일: src/test/resources/initdata.sql
• 테스트 코드: src/test/java/sptest/MemberRegSvcIntTest.java
delete from MEMBER; insert into MEMBER values ('gildong', '1234', '홍길동');
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring.xml") @Sql(scripts = "classpath:/initdata.sql") // 테스트 메서드 실행 시 데이터 초기화 public class MemberRegSvcIntTest { @Autowired private MemberRegisterService memberRegisterService; @Autowired private MemberDao memberDao; @Test public void register() { // 데이터가 초기화되므로, "user" ID를 갖는 데이터는 항상 DB에 존재하지 않음 RegisterRequest regReq = createRequest("user", "사용자"); memberRegisterService.register(regReq); assertThat(memberDao.selectById("user"), notNullValue()); } @Test(expected = DuplicateIdException.class) public void duplicateIdTest() { // 데이터를 초기화할 때 ID가 "gildong"인 데이터를 삽입하므로, 항상 ID 중복 에러 발생 RegisterRequest regReq = createRequest("gildong", "홍길동"); memberRegisterService.register(regReq); } public RegisterRequest createRequest(String id, String name) { RegisterRequest regReq = new RegisterRequest(); regReq.setId(id); …생략 return regReq; } }
4. 활용 예제
4.3 스프링 통합 테스트: DB 초기화 예제 (2/2)
43
• 실행 결과
– Run As → JUnit Test 으로 MemberRegSvcIntTest 실행
initdata.sql 실행 확인
4. 활용 예제
4.4 스프링 MVC 테스트: GET/POST 예제 (1/3)
44
• MockMVC 예제: src/test/java/sptest/MemberRegMvcTest.java
– "/regist"에 의해 실행되는 코드는 MemberRegisterController.java
• 테스트 코드:
– 설정 부분, GET 요청 처리 테스트
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring.xml") @WebAppConfiguration @Sql(scripts = "classpath:/initdata.sql") public class MemberRegMvcTest { @Autowired private WebApplicationContext ctx; private MockMvc mockMvc; @Before public void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build(); } @Test public void form() throws Exception { mockMvc.perform(get("/regist")) .andExpect(view().name("registrationForm")); }
4. 활용 예제
4.4 스프링 MVC 테스트: GET/POST 예제 (2/3)
45
• 테스트 코드
– POST 폼 전송 테스트
@Test public void postErrorForm() throws Exception { mockMvc.perform(post("/regist").param("id", "user") .param("password", "").param("confirmPassword", "").param("name", "사용자")) .andExpect(model().attributeHasFieldErrorCode("member", "password", "required")) .andExpect(model().attributeHasFieldErrorCode("member", "confirmPassword", "required")) .andExpect(view().name("registrationForm")); } @Test public void postDupId() throws Exception { mockMvc.perform(post("/regist").param("id", "gildong") .param("password", "1").param("confirmPassword", "1").param("name", "사용자")) .andExpect(model().attributeHasFieldErrorCode("member", "id", "duplicateId")) .andExpect(view().name("registrationForm")); } @Test public void postSuccess() throws Exception { mockMvc.perform(post("/regist").param("id", "user") .param("password", "1234").param("confirmPassword", "1234").param("name", "사용자")) .andExpect(view().name("registered")); } }
4. 활용 예제
4.4 스프링 MVC 테스트: HTTP 세션/리다이렉트 확인
47
• MockMVC 예제: src/test/java/sptest/LoginMvcTest.java
– "/login"에 의해 실행되는 코드는 LoginController.java
• 테스트 코드:
– 설정 부분은 앞서 MemberRegMvcTest.java와 동일
– 로그인 실패/성공시 세션 생성 유무 테스트
@Test public void postInvalidIdOrPassword() throws Exception { mockMvc.perform(post("/login").param("id", "user").param("password", "1234")) .andExpect(model().attributeHasErrors("login")) .andExpect(view().name("loginForm")) .andExpect(request().sessionAttribute("auth", nullValue())); }
@Test public void loginSuccess() throws Exception { MvcResult mvcResult = mockMvc.perform( post("/login").param("id", "gildong").param("password", "1234")) .andExpect(redirectedUrl("/main")) .andReturn(); HttpSession session = mvcResult.getRequest().getSession(); Auth auth = (Auth) session.getAttribute("auth"); assertThat(auth, notNullValue()); assertThat(auth.getId(), equalTo("gildong")); } }
4. 활용 예제
4.4 스프링 MVC 테스트: 쿠키 생성, 삭제
48
• MockMVC 예제: src/test/java/sptest/Login2MvcTest.java
– "/login2"에 의해 실행되는 코드는 Login2Controller.java
– "/logout2"에 의해 실행되는 코드는 Logout2Controller.java
• 테스트 코드:
– 설정 부분은 앞서 MemberRegMvcTest.java와 동일
– 로그인/로그아웃시 쿠키 생성/삭제 유무 테스트
@Test public void loginSuccess() throws Exception { mockMvc.perform(post("/login2").param("id", "gildong").param("password", "1234")) .andExpect(redirectedUrl("/main")) .andExpect(cookie().exists("AC")); } @Test public void logoutSuccess() throws Exception { mockMvc.perform(post("/logout2").cookie(new Cookie("AUTH", "somevalue"))) .andExpect(redirectedUrl("/main")) .andExpect(cookie().maxAge("AC", 0)) .andExpect(cookie().path("AC", "/")); }
4. 활용 예제
4.4 스프링 MVC 테스트: JSON 요청 전송/응답 검증
49
• MockMVC 예제: src/test/java/sptest/RestAPIMvcTest.java
– "/restapi/members"에 의해 실행되는 코드는 MemberRestAPI.java
• 테스트 코드:
– 설정 부분은 앞서 MemberRegMvcTest.java와 동일
– JSON 요청/응답 검증
@Test public void newMember() throws Exception { mockMvc.perform(post("/restapi/members") .contentType(MediaType.APPLICATION_JSON) .content("{\"id\": \"user\", \"password\": \"1234\", \"name\": \"사용자\" }") ) .andExpect(status().isCreated()) .andExpect(jsonPath("$.id").value("user")) .andExpect(jsonPath("$.name").value("사용자")); }