1. 단위 테스트 환경 설정
1) Junit4 @RunWith(SpringRunner.class), @SpringBootTest
Service Class에서 [cmd + Shift + T]를 통해 테스트 코드를 생성할 수 있다.(Junit4)
이때 단위 테스트를 정의하는 클래스에 @RunWith(SpringRunner.class)와 @SpringBootTest 어노테이션 함께 추가하는데 왜 그럴까?
@RunWith 어노테이션은 테스트 실행방법을 확장할 때 사용하는 어노테이션으로, 만약 @SpringBootTest만 사용하면 application context를 전부 로딩해서 자칫 잘못하면 무거운 프로젝트로서의 역할을 할 수 있다. 하지만 Junit4에서 지원하는 @RunWith(SpringRunner.class)를 사용한다면 @Autowired, @MockBean에 해당되는 것들에만 application context를 로딩하게 되므로 부담이 덜하다.
하지만 SpringBoot 2.1 버전부터는 Junit 버전이 5.x 로 변경되었고, @RunWith 어노테이션이 @SpringBootTest 어노테이션에 포함되도록 변경되었기 떄문에 더이상 별도로 정의할 필요가 없다.
2. 회원 가입 테스트
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class MemberServiceTest {
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Test
@Rollback(value = false)
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
public void 중복_회원_예외() throws Exception {
// given
// when
// then
}
}
1) 회원 가입(Unit Test에서 @Transactional의 원리)
앞선 포스트에서도 보았듯이 JPA의 모든 데이터의 변경이나 로직은 트랜잭션 안에서 이루어져야 하기 때문에 @Transactional를 붙이게 된다. 회원가입() 메서드가 종료될 때(트랜잭션이 종료될 때) 메서드 내에서의 Entity의 수정 사항이 데이터베이스에 Commit(반영) 되거나 Rollback(초기화)하게 된다.
하지만 스프링에서 기본적으로 @Transactional은 Test에서 트랜잭션을 DB에 Commit을 하지 않고 Rollback을 한다. 즉, Persistent Context가 Flush를 하지 않는다. Flush란 Persistent Context에 있는 변경이나 등록 내용을 DB에 반영하는 것이다.
실제 DB에 어떠한 Entity를 Insert 하는 쿼리문을 동작하게 만들기 위해서는 @Rollback(value = false)를 설정하여 트랜잭션의 결과가 DB에 반영되도록 해야 한다.
이제 Query 문을 확인할 수 있다.
@Rollback을 하더라도 쿼리문을 확인하도록 만들 수 있다. EntityManager를 @Autowired로 주입 받은 후 flush를 자체적으로 호출하는 것이다. 이러면 실제 트랜잭션은 rollback 된다.
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@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));
}
2) 중복회원 검증(@Test(expected = IllegalStateException.class) 사용)
지금까지 try-catch 문을 사용해서 에러를 처리해왔다.
@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 (IllegalStateException e) {
return;
}
// then
fail("예외가 발생해야 한다.");
}
@Test의 expected 옵션을 통해 에러 코드를 가독성 있게 처리할 수 있다.
@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("예외가 발생해야 한다.");
}
3. 메모리 DB를 사용한 Unit Test
현재 테스트에서 사용되는 DB는 외부에서 가져온 것이다. 이런 테스트를 병렬로 여러개를 돌리기에도 어렵고, DB를 외부에 설치해야하는 등 번거로운 작업을 수행해야한다. SpringBoot에서는 Java 안에 DB를 새로 만들어서 띄우는 방법이 있다. 즉, 테스트를 완전히 격리된 환경에서 수행할 수 있는 것이다.
이러한 방식을 메모리 DB를 사용한다고 말한다.
main 디렉터리와 동일하게 resources 디렉터리를 만들어주고, application.yml 파일을 추가하고, 파일의 datasource의 url을 메모리로 변경해준다. (h2database.com의 cheat sheet에서 확인할 수 있다)
그런데 SpringBoot에서는 기본적으로 별도의 설정이 없으면 메모리 모드로 실행하게 된다. 즉, 다음과 같이 생략할 수 있다. (다 생략도 가능하다)
spring:
# datasource:
# url: jdbc:h2:mem:test
# 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 # hibernate가 남기는 모든 로그가 debug 로그로 다 확인할 수 있다.
org.hibernate.type: trace
'🌱 Spring > JPA ①' 카테고리의 다른 글
[Spring] 주문 Entity, Repository, Service 개발 (0) | 2023.08.09 |
---|---|
[Spring] 상품 Entity, Repository, Service 개발 (0) | 2023.08.05 |
[Spring] 회원 Service & Repository 개발 (0) | 2023.08.04 |
[Spring] Entity 설계 및 구현 (0) | 2023.08.03 |
[Spring] 프로젝트 기본 구성 (0) | 2023.08.02 |