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 어노테이션 옵션으로 지정함@SpringBootTestSpringBoot를 띄운 상태로 테스트하기 위함@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.ymlspring:
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