Just Do IT!
[Spring Boot] H2 Database를 사용하여 테스트 코드 작성하기 본문
H2 Database를 사용하는 이유?
- H2 DB는 In-Memory DB로 스프링 애플리케이션과 함께 실행되며, 매우 가벼운 용량을 가지고 있다.
- 복잡한 mocking 없이도 서비스 및 레포지토리 레이어를 테스트할 수 있기 때문에 mocking으로 인한 부분적인 오류나 가짜 성공(실 서비스에서는 오류가 발생하지만 mocking으로는 성공으로 표기되는 상황)을 방지할 수 있다.
- 테스트 과정에서 데이터가 영향을 받지 않도록 한다.
- 만약 다른 Database를 사용하는 경우, 테스트 데이터가 롤백되어 DB에 남아있지 않아도 auto increment로 지정한 id가 계속 증가되는 등의 문제가 발생할 수 있다.
- => 테스트용 DB가 따로 필요하다.
기존 Database 대신에 H2 Database를 이용하여 테스트를 하는 것이 훨씬 편하다.
H2 DB 세팅하기
1. gradle 세팅
testImplementation 'com.h2database:h2:2.1.214'
테스트 환경에서만 해당 종속성을 이용할 수 있도록 testImplementation으로 종속성을 추가해준다.
개발 또는 운영 환경에 포함되지 않게 할 수 있다.
2. application.yml 분기
원래 이 부분에 대해 따로 블로그를 작성하려고 했으나, db 세팅에 포함되므로 함께 작성한다.
로컬환경과 개발환경, 그리고 운영 환경 설정을 서로 다르게 가져가야 할 경우가 존재한다.
이럴 떄 .yml 파일에서 서로 다른 설정에 따른 파일을 만드는 것이 일반적으로 Spring에서 활용하는 환경 분리 방법이다.
application.yml
server:
port: 8080
spring:
profiles:
default: local
---
spring:
config:
activate:
on-profile: dev
---
spring:
config:
activate:
on-profile: local
---
spring:
config:
activate:
on-profile: test
기본적으로 설정이 application-local.yml 설정을 따라간다.
만약 변경하고 싶은 경우 default를 test나 dev로 변경하면 된다.
application-test.yml
spring:
jpa:
open-in-view: true
hibernate:
ddl-auto: create
properties:
hibernate:
show_sql: true
format_sql: true
datasource:
url: jdbc:h2:mem:testdb
username: sa
h2:
console:
enabled: true
test 환경에서 H2 DB를 사용할 수 있도록 세팅해준다.
ddl-auto를 이용해 테스트 실행과 함께 DB 테이블을 생성할 수 있도록 한다.
실제 개발 환경에는 MySQL을 쓰고 ddl-auto가 none으로 되는 게 맞지만, 로컬 개발 환경에서는 매번 확인해보는 게 좋기 때문에 h2 인메모리 데이터베이스를 쓰면서 ddl-auto를 create로 하는 것이 좋다.
datasource는 h2를 사용하고 username은 위와 같이 맞춰야 별도의 세팅없이 이용 가능하다.
Test code 작성 예시
프로젝트에 적용했던 테스트 코드를 예시로 들어본다.
repository에 관한 테스트 코드이다. 우선 repository에 관련된 테스트 코드를 작성하려면 @DataJpaTest 어노테이션에 대해 알아야 한다.
@DataJpaTest
- JPA와 관련된 설정(Entity, Repository)만 로드해서 테스트를 진행
- 필요한 객체를 @Autowired로 주입받을 수 있음
- 이때 스프링 컨테이너를 사용하지 않으므로 @SpringTest보다 빠른 테스트를 할 수 있다
- @Transactional 어노테이션이 포함되어 있어서 테스트 코드가 종료되면 자동으로 데이터베이스의 롤백이 진행된다.
- @ExtendWith(SpringExtension.class) 어노테이션이 포함되어 있음, 따로 작성하지 않아도 된다.
- 테스트시 참조할 DB로 임베디드 H2를 사용함
- 따라서 @DataJpaTest 를 사용할 때 H2 디펜던시가 없으면 오류 발생한다.
- 만약 다른 DB를 참조하고 싶을 때는 @AutoConfigureTestDatabas(replace = Replace.NONE) 어노테이션을 추가로 붙이면 된다.
- 이 어노테이션은 자동으로 DB를 구성할 것인지에 대한 설명을 해준다.
repository 관련 테스트 코드 예시
여기서 테스트 해보려는 것은 문제집 전체 조회 기능이다.
그래서 임의로 문제집을 생성해서 저장하고, 해당 리스트들 조회가 가능한지 테스트해본다.
package com.dodream.testdream.book.repository;
import static org.junit.jupiter.api.Assertions.*;
import com.dodream.testdream.book.entity.Book;
import com.dodream.testdream.common.enumtype.Category;
import com.dodream.testdream.user.entity.User;
import com.dodream.testdream.user.repository.UserRepository;
import jakarta.transaction.Transactional;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.jdbc.Sql;
import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
class BookRepositoryTest {
@Autowired
private BookRepository bookRepository;
@Autowired
private UserRepository userRepository;
@DisplayName("전체 문제집 조회 테스트")
@Test
public void getAllBooksTest() {
// given (사전 준비)
User user1 = User.builder().username("hello").provider("provider1").providerId("1").build();
userRepository.save(user1); // 사용자 저장
Book book1 = Book.builder()
.title("Test Book1")
.user(user1)
.category(Category.CATEGORY_CS)
.secret(false)
.build(); // 공개 문제집
bookRepository.save(book1);
Book book2 = Book.builder()
.title("Test Book1")
.user(user1)
.category(Category.CATEGORY_CS)
.secret(false)
.build(); // 공개 문제집
bookRepository.save(book2);
Book book3 = Book.builder()
.title("Test Book3")
.user(user1)
.category(Category.CATEGORY_CS)
.secret(false)
.build(); // 공개 문제집
bookRepository.save(book3);
Book book4 = Book.builder()
.title("Test Book4")
.user(user1)
.category(Category.CATEGORY_CS)
.secret(false)
.build(); // 공개 문제집
bookRepository.save(book4);
Book book5 = Book.builder()
.title("Test Book5")
.user(user1)
.category(Category.CATEGORY_CS)
.secret(true)
.build(); // 비공개 문제집
bookRepository.save(book5);
// when (테스트 진행할 범위)
List<Book> books = bookRepository.findAllBySecretFalse();
// then (범위에 대한 결과 검증)
assertThat(books).hasSize(4); // 공개 문제집 4개
assertThat(books.get(0).getTitle()).isEqualTo("Test Book1"); // 첫 번째 제목 검증
}
}
사용자는 테스트 대상이 아니라 임의로 그냥 한 사용자만 지정해주었고 문제집을 5개 만들어주었다.
테스트할 전체 문제집 조회에서, 비공개 문제집은 조회하지 않아야 하므로 그 부분도 추가해주었다.
이렇게 테스트 코드를 작성하고 실행해보니
제대로 테스트 되는 것을 확인할 수 있다.
우선은 처음으로 테스트 코드를 처음부터 끝까지 작성해봐서 약간 뿌듯하다.
단위 테스트할때 원래 service, controller도 테스트 코드를 작성해야 하는데 이건 추후에 더 공부한 다음에 작성해볼 예정이다.
이번에는 기능을 구현한 다음에 테스트 코드를 작성했는데 다음에는 꼭 테스트 코드 먼저 작성해보고 기능을 구현하고 싶다. 그 때는 service, controller, repository 모두 테스트 코드 작성해봐야지...
겸사겸사 h2 DB에 대해서도 알게 되었는데 확실히 테스트 할때는 더 편한 것 같다. 앞으로 개인 프로젝트를 하거나 실습 공부를 할 때 애용해야겠다.
'개발 공부 > Spring' 카테고리의 다른 글
[Spring Boot] 북마크 생성/삭제 토글 형식으로 구현하기 (0) | 2024.10.22 |
---|---|
@RestControllerAdivce 적용해서 Spring 전역 예외처리하기 (1) | 2024.10.21 |
JWT에서 Access Token, Refresh Token이 필요한 이유 (1) | 2024.09.11 |
JWT + spring security 로그인 구현 실습 (0) | 2024.09.10 |
[Spring Boot] 프로젝트 빌드해서 jar 파일 생성/실행해보기 (0) | 2024.08.30 |