[Spring Boot] DAO(Repository) 테스트 코드 작성
Spring Boot DAO(Data Access Object)인 Repository 계층의 단위 테스트하는 방법을 정리해보았다.
[Spring Boot] DAO(Repository) 테스트 코드 작성
- Spring Boot
DAO(Data Access Object)인Repository계층의 단위 테스트하는 방법을 정리해보았다. Repository계층은 다른 레이어의 의존이 없이 독립적인 계층이므로Controller,Service계층의 단위 테스트보다 간단한 주입만을 요한다.Repository의 각 메서드를 테스트하게 되면, 데이터베이스의 데이터를 변경하게 되므로, 기본적으로 각 메서드들은 트랜잭션 안에서 실행된다.- 즉, 테스트가 종료되면 트랜잭션이 rollback되므로 데이터베이스의 데이터 변경을 걱정하지 않아도 된다.
@DataJpaTest
- 해당 애노테이션은 JPA 테스트에 필요한
EntityManager,JpaRepository,Hibernate설정 등 JPA관련한 빈을 로드하도록 해준다. - Spring 통합 테스트 시 사용하는
@SpringBootTest와 달리, MVC나 Service계층의 빈은 로드되지 않아 빠른 실행이 가능해진다. - 또한
@DataJpaTest는 인메모리 데이터베이스를 사용한다. 이를 사용하기 위해 h2 db를 사용할 예정이다. ```groovy build.gradle
// h2 database
testImplementation ‘com.h2database:h2’
1
2
3
4
5
6
7
8
- 만약 인메모리 데이터베이스 대신 로컬의 데이터베이스를 사용하고 싶으면 아래와 같이 설정하면 된다.
```java
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class RepsitoryTest {
// ...
}
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)가 인메모리 데이터베이스 사용을 막는 역할을 한다.@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)는 인메모리 데이터베이스 사용하도록 설정한다.
@ActiveProfiles(“test”)
- 해당 애노테이션을 클래스에 적용하여 테스트 관련 db설정을 분리 가능하다.
- 아래와 같이 application-test.properties를 적용할 수 있다. ```
datasource
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb;
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.username=sa
spring.h2.console.enabled=true
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.H2Dialect
1
2
3
4
5
6
7
8
9
### @Import
- `@DataJpaTest`애노테이션으로 JPA관련 빈을 로드하는 것만으로 `Repository`를 테스트할 수 없는 환경일 가능성이 있다.
- 예를 들어 `Entity`에서 생성, 변경 시간을 캐치하기 위한 `JpaAuditing`같은 속성이 있다.
```java
@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {
}
repository를 실행하는데 전혀 문제 없는 기능이지만 해당 속성이 없으면entity를 생성할 때 생성, 변경시간이 null이 되는 현상이 발생할 수 있다. (에러는 나지 않는다..)- 중요한 예시로
Querydsl을 사용하여repository를 구현했을 때는, 아래와 같은 Config를 생성해야한다.1 2 3 4 5 6 7 8 9 10 11
@Configuration public class QueryDslConfig { @PersistenceContext private EntityManager entityManager; @Bean public JPAQueryFactory jpaQueryFactory() { return new JPAQueryFactory(entityManager); } }
- 이는
repository실행에 있어 필수적인 설정이지만@DataJpaTest를 사용해서 빈으로 로드되지 않기 때문에 직접Import하는 이유라고 볼 수 있겠다.1 2 3 4 5 6
@DataJpaTest @ActiveProfiles("test") @Import({QueryDslConfig.class, JpaAuditingConfig.class}) class RepsitoryTest { // ... }
예시
- 위와 같은 사항을 고려하여 작성한 간단한 테스트코드이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
@DataJpaTest @ActiveProfiles("test") @Import({QueryDslConfig.class, JpaAuditingConfig.class}) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class LogCustomRepositoryTest { @Autowired LogRepository logRepository; @Autowired MemberRepository memberRepository; @Autowired CategoryRepository categoryRepository; @Autowired JdbcTemplate jdbcTemplate; @AfterEach void setUp() { logRepository.deleteAll(); categoryRepository.deleteAll(); memberRepository.deleteAll(); } @Test @Order(1) @DisplayName("check db") void checkDatabaseUsed() throws SQLException { String url = Objects.requireNonNull(jdbcTemplate.getDataSource()).getConnection().getMetaData().getURL(); System.out.println("db = " + url); } @Test @Order(2) @DisplayName("memberId로 사용자의 로그 찾기") void findByMemberId() { Member saveMember = memberRepository.save(MemberTestConvertor.createMemberInTest(1L)); Category saveCategory = categoryRepository.save(CategoryTestConvertor.createCategoryInTest(1L, saveMember)); logRepository.save(LogTestConvertor.createLogInTest(1L, saveCategory)); logRepository.save(LogTestConvertor.createLogInTest(2L, saveCategory)); List<Log> result = logRepository.findByMemberId(saveMember.getId()); Assertions.assertThat(result.size()).isEqualTo(2L); // category 정보 Assertions.assertThat(result.get(0).getCategory().getId()).isEqualTo(1L); Assertions.assertThat(result.get(1).getCategory().getId()).isEqualTo(1L); // member 정보 Assertions.assertThat(result.get(0).getCategory().getMember().getId()).isEqualTo(1L); Assertions.assertThat(result.get(1).getCategory().getMember().getId()).isEqualTo(1L); } }
- 테스트를 진행할 메서드들은
@Test애노테이션을 붙여야하며, 시각적으로 확인하기 위해 추가로@DisplayName을 사용하기도 한다. - 직접 구현한
@Repository구현체들은@Autowired로 직접 주입하여 테스트를 진행한다. - 해당 테스트클래스를 실행했을 때 어떤 db에서 해당 테스트가 이루어지는지 확인하기 위해
jdbcTemplate를 주입하여 url를 출력하였다.
@BeforeEach / @AfterEach
- 각 메서드를 실행하기 전, 후로 실행할 메서드를 정의 가능하다.
- 메서드마다 독립성이 존재해야 하므로 해당 애노테이션을 사용해
repository를 비우거나EntityManager를 주입하여 영속성 컨텍스트를flush후clear하는 용도로 사용한다.
@Order
@Order애노테이션을 이용해 테스트 메서드의 실행 순서를 정할 수 있다.Order(n)의 숫자가 작을수록 우선순위가 크다.- 해당 애노테이션을 사용하기 위해 테스트클래스에 ``@TestMethodOrder(MethodOrderer.OrderAnnotation.class) 를 붙여야 사용 가능하다.
원문: Velog
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.
![[Spring Boot] DAO(Repository) 테스트 코드 작성](/assets/img/posts/velog/7b69745d76a79492b405.png)