Spring Rest Docs는 테스트를 통과한 코드만 API 문서에 작성할 수 있습니다. 이러한 방식은 개발자로
하여금 코드의 신뢰도와 품질을 향상시킬 수 있다고 생각합니다.
그러면 개발 이후에 테스트 코드를 반드시 작성해야 하는데, 부끄럽지만 여태 개발 하면서 테스트 코드를
작성해 본 적이 없었습니다. 단순히 Postman으로 API 요청, 응답을 받는 선에서 그쳤는데,
지금 생각 해 보면, controller → service (내부 모름) → controller 인 방식의 테스트, 즉 컨트롤러 단의
테스트만 진행하던 것이었습니다. 따라서 이번 기회에 전범위적인 테스트 방식을 익히고, 내 코드를 내가 믿을 수
있도록 열심히 배워보겠습니다.
일단은 첫번째로, Repository 단의 Test 입니다.
Spring Data JPA를 사용하여 DB와 통신하는데, 크게 두가지 방법이 있습니다.
- @DataJpaTest
- @SpringBootTest + Memory DB 연결
두 방식 모두 Repository 계층을 테스트하는 것은 똑같지만, 차이점이 있습니다.
@DataJpaTest는 JPA 영속성 컨텍스트의 특성을 이용합니다.
- 영속성 컨텍스트는 flush() 가 실행되기 전까지 실행될 쿼리를 가지고 있다가, 한번에 쿼리를 실행하도록 설계되어 있다. → Database 의 Commit 을 생각하시면 편할 것 같습니다.
- 따라서 flush() 가 실행되기 전까지 쿼리가 실행되지 않는다는 것은 @Transaction 안에서 Rollback 되어야 한다면 애초에 쿼리가 실행되지 않을 수 있습니다.
테스트 코드는 기본적으로 코드를 실행 한 후, 이전 상황으로 RollBack 합니다.
이래 저래 객체에 값을 넣어 생성해도, 테스트가 트랜잭션 안에서 롤백 된다면,
쿼리가 실행되지 않은 상태로 돌아가게 됩니다.
보시다시피 DataJpaTest 내부에는 Transactional 이 있기 때문에, 쿼리가 실행되지 않습니다.
결과적으로 DataJpaTest로 작성한 테스트는, repository.save() 시 영속성 컨텍스트에는 반영되지만, 쿼리는 보이지 않습니다.
시작에 앞서
아래와 같이 build.gradle 에 테스트를 위한 종속성을 추가하겠습니다.
testAnnotationProcessor('org.projectlombok:lombok') //1
testImplementation 'org.springframework.boot:spring-boot-starter-test'//2
testImplementation 'junit:junit:4.13.1' //3
test { //4
useJUnitPlatform()
}
- Test 시 롬복을 사용하기 위한 종속성입니다.
- 스프링 부트 테스트 종속성입니다.
- 테스트 라이브러리는 Junit과 AssertJ가 있는데, JUnit을 이용하므로 추가합니다.
- JUnit 5부터 추가된 종속성입니다.
예제 코드
Member..java
package jpabook.jpashop.domain.user.domain;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.*;
@Entity
@NoArgsConstructor(access =AccessLevel.PROTECTED)
@Getter
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
private String name;
private String email;
private String password;
private String phoneNum;
@Builder
public Member(Long id, String name, String email, String password, String phoneNum) {
this.id = id;
this.name = name;
this.email = email;
this.password = password;
this.phoneNum = phoneNum;
}
}
MemberRepository
package jpabook.jpashop.domain.user.domain.repository;
import jpabook.jpashop.domain.user.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MemberRepository extends JpaRepository<Member,Long> {
Member findByEmail(String email);
}
Repository 계층 테스트의 핵심은 데이터의 CRUD, 즉 내가 작성한 코드가
올바르게 데이터베이스에 반영이 되었는가? 입니다. 서버가 데이터베이스와 소통하는 방법은 여러가지가 있지만,
그 중에서 저희는 Spring Data JPA를 사용하고 있습니다.
예시 테스트
@ExtendWith(SpringExtension.class)
@DataJpaTest
class userRepositoryTest {
@Autowired
private MemberRepository memberRepository; //1
@Test
public void saveTest(){ //2
Member member = Member.builder()
.email("test@naver.com")
.name("John Doe")
.password("testPassword")
.phoneNum("010-1234-1234")
.build();
Member saveMember = memberRepository.save(member); //3
assertThat("test@naver.com").isEqualTo(saveMember.getEmail()); //4
assertThat("John Doe").isEqualTo(saveMember.getName()); //5
}
@Test
@DisplayName("findByEmailTest 입니다.")
public void findByEmailTest(){ //6
Member member = Member.builder()
.email("test@naver.com")
.password("testpw")
.phoneNum("010-1234-1234")
.name("john doe")
.build();
Member saveMember = memberRepository.save(member);
assertThat("john doe").isEqualTo(memberRepository.findByEmail("test@naver.com").getName()); //7
}
지레 겁을 먹었던 것인가, 생각보다 단순했습니다.
@ExtendWith(SpringExtension.class) 는 JUnit5에서 지원하는 어노테이션으로, 테스트 실행 시
Spring 의 테스트 지원을 활성화 합니다. 쉽게 말해, Autowired, Mockbean 등의 빈 주입 또한 가능해집니다.
이번 테스트에서는 @SpringBootTest 를 사용하지 않기 때문에, ExtendWith을 사용했습니다.
@DataJpaTest를 이용해 Jpa테스트를 진행합니다.
- memberRepository 빈을 주입받습니다.
- saveTest 테스트 메소드 안에서 저장 테스트를 진행합니다.
- Repository.save()를 이용해 저장합니다.
- Assertions.AssertThat()을 이용하여 저장한 값과 제시한 값이 같은지 비교합니다 .
- 정적 메소드 import 하여 assertThat() 으로 축약했습니다.
- 똑같이 이름을 비교합니다.
- MemberRepository에 있는 FindByEmail 을 테스트 해보겠습니다.
- 똑같이 객체를 생성 후, 저장합니다.
- isEqualTo() 이하에 memberRepository에서 작성한 findByEmail로 값을 가져와 비교합니다.
이러한 방식으로, Repository 계층의 테스트를 진행할 수 있었습니다.
테스트가 뭔가 더 희열이 있네요.. 빠르게 계속 익혀나가봅시다!
'Server > Spring' 카테고리의 다른 글
Redis Key Event Notification으로 지연 처리 구현하기 (0) | 2024.09.12 |
---|