여태 해왔던 기능 검증을 위한 단위 테스트와 유사하지만,
실제 운영 환경에서 사용될 클래스들을 통합하여 테스트해주는 @SpringBootTest 어노테이션에 대해 알아보자.
@SpringBootTest는 스프링 컨테이너와 DB까지 연결하여 통합 테스트를 할 수 있게 하는 어노테이션이다.
앞서 언급했듯이 실제 운영 환경에서 사용될 클래스들을 통합하여 테스트할 수 있다.
테스트 케이스에 @Transactional이 붙으면 테스트 시작 전에 트랜잭션을 시작하고 완료 후에는 롤백하게 된다.
@Transactional 어노테이션을 사용하면 DB에 데이터가 남지 않아 다음 테스트에 영향을 주지 않는다.
이는 단위 테스트의 @BeforeEach, @AfterEach 어노테이션과 유사한 기능을 한다.
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class MemberServiceTest {
MemberService memberService;
MemoryMemberRepository memberRepository;
// 테스트의 독립성을 위해 @BeforeEach와 @AfterEach를 사용하여 repository를 삭제, 재생성해줌
@BeforeEach
public void beforeEach() {
memberRepository = new MemoryMemberRepository();
memberService = new MemberService(memberRepository);
}
@AfterEach
public void afterEach() {
memberRepository.clearStore();
}
@Test
public void 회원가입() throws Exception {
//Given
Member member = new Member();
member.setName("hello");
//When
Long saveId = memberService.join(member);
//Then
Member findMember = memberRepository.findById(saveId).get();
assertEquals(member.getName(), findMember.getName());
}
@Test
public void 중복_회원_예외() throws Exception {
//Given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//When
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class,
() -> memberService.join(member2));//예외가 발생해야 한다.
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
}
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/*
* spring 컨테이너와 DB를 연결한 통합 테스트
* MemberServiceTest 와 기능은 동일함
*/
@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Test
public void 회원가입() throws Exception {
//Given
Member member = new Member();
member.setName("hello");
//When
Long saveId = memberService.join(member);
//Then
Member findMember = memberRepository.findById(saveId).get();
assertEquals(member.getName(), findMember.getName());
}
@Test
public void 중복_회원_예외() throws Exception {
//Given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//When
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class,
() -> memberService.join(member2));//예외가 발생해야 한다.
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
}
위의 예시를 보면,
통합 테스트로 진행하면 전체적인 흐름을 쉽게 검증해볼 수 있는 것을 알 수 있다.
그렇다면 통합 테스트를 사용하면 되는데 단위 테스트를 따로 하는 이유는 뭘까?
👉🏻 통합 테스트는 어플리케이션의 설정이나 Bean을 모두 로드하기 때문에 단위 테스트에 비해 검증 시간이 오래 걸린다. 또한, 테스트 단위가 크기 때문에 디버깅이 비교적 어렵다.