Spring Test

49
Spring Test 1. 도구 개요 2. 설치 준비 3. 주요 기능 4. 활용 예제

Transcript of Spring Test

Spring Test

1. 도구 개요

2. 설치 및 준비

3. 주요 기능

4. 활용 예제

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 메이븐 의존 설정

2.2 이클립스 Favorites 설정

4

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. 주요 기능

세부 목차

3.1 스프링 통합 테스트

3.2 트랜잭션 처리

3.3 스프링 MVC 테스트

3.4 테스트 DB 데이터 처리

8

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 예제 소개

4.2 예제 구성

4.3 스프링 통합 테스트

4.4 스프링 MVC 테스트

37

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 테스트: GET/POST 예제 (3/3)

46

•실행 결과

테스트용 DispatcherServlet 사용

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("사용자")); }