JUnit4
사용
package jpabook.jpashop.service;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.repository.MemberRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class MemberServiceTest {
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Test
public void 회원가입() throws Exception {
//given
Member member = new Member();
member.setName("kim");
//when
Long saveId = memberService.join(member);
//then
assertEquals(member, memberRepository.findOne(saveId));
}
@Test(expected = IllegalStateException.class)
public void 중복_회원_예외() throws Exception {
//given
Member member1 = new Member();
member1.setName("kim");
Member member2 = new Member();
member2.setName("kim");
//when
memberService.join(member1);
memberService.join(member2);
//then
fail("중복 회원 가입 예외 발생해야 한다.");
}
}
@RunWith(SpringRunner.class)
SpringRunner
클래스를 @RunWith
어노테이션 옵션으로 지정함@SpringBootTest
SpringBoot
를 띄운 상태로 테스트하기 위함@Autowired
전부 다 실패해서 MemberService
와 MemberRepoistory
를 가져올 수 없음@Transactional
테스트 시 주의 사항
H2
데이터베이스를 켜놓은 상태에서 테스트를 수행해야 함@Transactional
선언 필수
@Test
public void 회원가입() throws Exception {
//given
Member member = new Member();
member.setName("kim");
//when
Long saveId = memberService.join(member);
//then
assertEquals(member, memberRepository.findOne(saveId));
}
pk(id)
값이 똑같으면 같은 영속성 컨텍스트 하나로 관리됨select
쿼리만 실행되고, insert
쿼리는 실행되지 않는 이유save
메서드는 EntityManager
를 persist
함persist
를 한다고 해서 DB에 쿼리문이 나가진 않음commit
될 때, flush
되면서 insert
쿼리가 나감insert
쿼리를 수행하고 싶다면 (실제 트랜잭션 rollback X) @Autowired EntityManager em;
@Test
@Rollback(value = false)
public void 회원가입() throws Exception {
//given
Member member = new Member();
member.setName("kim");
//when
Long saveId = memberService.join(member);
//then
em.flush();
assertEquals(member, memberRepository.findOne(saveId));
}
@Transactional
은 기본적으로 RollBack
을 수행하기 때문에 @Rollback(value = false)
를 추가해야 함insert
쿼리가 수행되지만, 실제 트랜잭션은 rollback
하고 싶다면 @Autowired EntityManager em;
@Test
public void 회원가입() throws Exception {
//given
Member member = new Member();
member.setName("kim");
//when
Long saveId = memberService.join(member);
//then
em.flush();
assertEquals(member, memberRepository.findOne(saveId));
}
em.flush()
로 디비에 영속성컨텍스트에 있는 쿼리를 날리고, 테스트가 끝날때 @Transactional
이 rollback
을 수행함사실 테스트는 디비에 데이터가 남으면 안되는 것이기 때문에 첫번째 방법으로 작성해야함
- 하지만 디비에 제대로 들어가고 있는지 확인은 필요함
- 작은 메모리 DB를 띄워서 테스트용으로 사용하는 것이 좋음(뒤에 자세히 언급)
- 예외처리 테스트가 성공하면 그대로 return이 되어서 테스트 성공
- 테스트가 실패하면 fail이 수행됟어 예외 발생이 실패했음을 알려야 함
IllegalStateException
를 잡아서 처리해줘야 함@Test
public void 중복_회원_예외() throws Exception {
//given
Member member1 = new Member();
member1.setName("kim");
Member member2 = new Member();
member2.setName("kim");
//when
memberService.join(member1);
try {
memberService.join(member2);
} catch (IllegalArgumentException e){
return;
}
//then
fail("중복 회원 가입 예외 발생해야 한다.");
}
member2
의 Name을 다르게 설정하여 중복된 회원 가입이 아닌 경우를 만들어서 테스트를 수행할 경우fail
즉, JUnit
에서 제공하는 AssertionError
가 발생함 @Test(expected = IllegalStateException.class)
public void 중복_회원_예외() throws Exception {
//given
Member member1 = new Member();
member1.setName("kim");
Member member2 = new Member();
member2.setName("kim");
//when
memberService.join(member1);
memberService.join(member2);
//then
fail("중복 회원 가입 예외 발생해야 한다.");
}
try-catch
문을 사용하여 예외처리를 하는 것 대신, @Test
어노테이션에 expected
옵션에 예외처리할 예외 클래스를 지정해주면, 더 간단한 코드로 예외처리가 가능함test/resources/application.yml
spring:
datasource:
url: jdbc:h2:mem:testdb
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
# show_sql: true
format_sql: true
logging.level:
org.hibernate.SQL: debug
org.hibernate.type: trace
test/resources/application.yml
설정 파일을 읽음src/resources/application.yml
을 읽음spring:
# datasource:
# url: jdbc:h2:mem:testdb
# username: sa
# password:
# driver-class-name: org.h2.Driver
#
# jpa:
# hibernate:
# ddl-auto: create-drop
# properties:
# hibernate:
# show_sql: true
# format_sql: true
logging.level:
org.hibernate.SQL: debug
# org.hibernate.type: trace
datasource
설정이 없으면 기본적으로 메모리 DB를 사용하기 때문에 위 코드처럼 log 설정 부분을 제외하고는 추가 설정이 필요가 없음driver-class
또한 현재 등록된 라이브러리를 보고 찾아줌ddl-auto
또한 create가 아닌 create-drop
모드로 동작하여, table create 후에 drop까지 이루어져서 데이터가 자동 초기화되도록 해줌테스트 코드 작성법 참고 : https://martinfowler.com/bliki/GivenWhenThen.html