이전까지 사용했던 MemberRepositoryTest, MemberServiceTest 등의 테스트는,
자바로 작성된 개별 기능만을 테스트하는 단위 테스트다.
즉 스프링 컨테이너가 메모리에 로드되지 않고 순수하게 자바 코드의 동작만을 테스트한다고 보면 된다.
이제는 개발한 프로그램이 DB와 연동이 되므로, 스프링 컨테이너를 띄워 DB 등 다른 시스템과의 연계까지 정상적으로
되는지 확인하는 통합 테스트가 필요하다.
테스트 케이스 작성
테스트 코드 경로 하위의 service 폴더에
기존 MemberServiceTest를 복사해 MemberServiceIntegrationTest 클래스를 생성한다.
복사한 MemberServiceIntegrationTest를 다음과 같이 수정한다.
MemberServiceIntegrationTest.java
package hdxian.hdxianspring.service;
import hdxian.hdxianspring.domain.Member;
import hdxian.hdxianspring.repository.MemberRepository;
import hdxian.hdxianspring.repository.MemoryMemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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;
@SpringBootTest
@Transactional
class MemberServiceIntegrationTest {
@Autowired
MemberService memberService;
@Autowired
MemberRepository memberRepository;
// @BeforeEach
// public void beforeEach() {
// memberRepository = new MemoryMemberRepository();
// memberService = new MemberService(memberRepository);
// }
// @AfterEach
// public void afterEach() {
// memberRepository.clearStore();
// }
@Test
void join() {
// given
Member member = new Member();
member.setName("spring");
// when
Long savedId = memberService.join(member);
// then
Member findMember = memberService.findOne(savedId).get();
Assertions.assertThat(findMember.getName()).isEqualTo(member.getName());
}
@Test
void 중복_회원_예외() {
// given
Member member1 = new Member();
member1.setName("hello");
Member member2 = new Member();
member2.setName("hello");
// when
memberService.join(member1);
IllegalStateException e = org.junit.jupiter.api.Assertions.assertThrows(IllegalStateException.class, () -> memberService.join(member2));
// then
Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
@Test
void findMembers() {
}
@Test
void findOne() {
}
}
클래스 선언부에 @SpringBootTest, @Transactional 어노테이션이 추가되었고,
memberSerivce, memberRepository가 @Autowired로 DI받으며,
memberRepository는 MemberRepository 타입으로 변경되었다.
또한, beforeEach(), afterEach()가 삭제되었다.
@SpringBootTest
- 스프링 컨테이너를 띄우면서 테스트를 실행한다.
@Transactional
- 테스트 케이스 시작 전에 트랜잭션을 시작하고, 테스트 완료 후에 항상 롤백한다.
즉 테스트 내용이 DB에 반영되지 않으므로 다음 테스트에 영향을 주지 않는다.
beforeEach(), afterEach()가 필요 없어지는 것.
테스트 코드를 작성할 때는 원본 코드처럼 엄격하게 코드를 짜지 않아도 된다.
memberService, memberRepository에 간단하게 @Autowired를 삽입하였다.
@Transactional 유무에 따른 차이
@Transactional은 앞서 말했듯 테스트 케이스가 종료된 후 실행 내용을 롤백한다.
정확히는 @Transactional이 없으면 테스트 내용을 오토 커밋(auto-commit)해서 자동으로 DB에 반영한다.
@Transactional은 트랜잭션의 시작을 명시적으로 나타내어 원하는 시점에서 commit을 하도록 (@Commit 이용)하는 설정.
@Transactional이 적용된 경우와 그렇지 않은 경우를 비교해보자.
Transactional이 적용된 경우
join() 의 내용은 "spring"이라는 이름으로 회원가입을 시도한다.
테스트 진행 시 성공이라고 뜨지만, DB에는 해당 내용이 반영되어있지 않다.
Transactional이 적용되지 않은 경우
테스트 후 DB에 해당 내용이 반영되어 있다.
이런 식으로 DB에 테스트 내용이 반영되어 있으면 다른 테스트 케이스에 영향이 갈 수 있으므로,
@Transactional을 이용해 테스트 케이스 종료 후 롤백시켜 이를 미연에 방지하는 원리이다.
@SpringbootTest
@SpringBootTest 어노테이션을 붙이면 다음과 같이 테스트 실행 시 스프링 컨테이너가 로드된다.
@SpringbootTest를 이용하면 스프링 컨테이너까지 모두 띄워지므로 작성한 애플리케이션의 모든 기능을 테스트할 수 있지만, 자바 코드만 실행하던 단위 테스트와 달리 시간이 오래 걸린다.
즉, 통합 테스트는 자주 실행하기엔 부담이 크기 때문에,
모든 테스트가 효율적으로 이루어질 수 있도록 단위 테스트를 잘 설계하는 노력 및 기술적 숙련도가 필요하다.
'[inflearn] 스프링 입문' 카테고리의 다른 글
15. 스프링 JPA (Java Persistence API) (0) | 2023.04.30 |
---|---|
14. 스프링 JdbcTemplate (0) | 2023.04.30 |
12. 순수 JDBC (0) | 2023.04.29 |
11. H2 데이터베이스 설치 (0) | 2023.04.29 |
10. 회원 관리 예제 - 웹 MVC 개발 (0) | 2023.04.16 |