[내일배움캠프] 테스트코드 - 실습
1. 레이어별 테스트
|
@SpringBootTest
|
All
|
통합 테스트, 전체
|
IntegrationTest
|
Bean 전체
|
|
@WebMvcTest
|
Controller
|
단위 테스트, Mvc 테스트
|
MockApiTest
|
MVC 관련된 Bean (+Service)
|
|
@DataJpaTest
|
Repository
|
단위 테스트, Jpa 테스트
|
RepositoryTest
|
JPA 관련 Bean
|
|
None
|
Service
|
단위 테스트, Service 테스트
|
MockTest
|
None (+Repository)
|
|
None
|
DTO, Entity, Util…
|
POJO, 도메인 테스트
|
None
|
None
|
2. @Transctional 과 Rollback 설정
Spring의 테스트 트랜잭션 처리 방식은 @Test 메서드 기준으로만 작동한다. 테스트에서는 데이터 롤백을 목적으로 @Transactional 을 사용하고, 만약 롤백할 필요가 없는 테스트라면 따로 트랜잭션 선언이 불필요하다.
이중 @Transactional 질문 드립니다! - 인프런 | 커뮤니티 질문&답변
누구나 함께하는 인프런 커뮤니티. 모르면 묻고, 해답을 찾아보세요.
www.inflearn.com
아래와 같은 상황에서 어떻게 Transaction이 설정될까?
@BeforeEach
public void setUp() {
// Test를 위한 데이터 초기화
printTransactionInfo("BeforeEach");
}
@AfterEach
public void reset() {
// Test이후 데이터 삭제
printTransactionInfo("AfterEach");
}
@Test
@Transactional
public void test() {
// 테스트
printTransactionInfo("Test");
}
트랜잭션이 먼저 수행되고, before -> 테스트 메서드 -> after 순서로 수행된다.
따라서, 보통 before에서 데이터를 준비하는데 이 부분이 롤백 범위에 포함되므로 편하게 사용하면 된다!
3. @Extension 의 쓰임
단위 테스트에 공통적으로 사용할 확장 기능을 선언해주는 역할을 한다. 인자로 확장할 Extension을 명시하면 된다.
주로, SpringExtension.class 또는 MockitoExtension.class를 많이 사용한다.
| JUnit5 + Mockito 단독 | @ExtendWith(MockitoExtension.class) + @Mock, @InjectMocks | Mockito 환경 구성 (스프링 컨텍스트 안 띄움) |
| Spring Boot Test | @SpringBootTest + @MockBean | Spring context 내 mock 주입 |
| SpringExtension.class | @ContextConfiguration(classes = config.class) | @SpringBootTest 없이 정해진 설정 클래스만 로딩 |
4. @DataJpaTest
이 어노테이션을 사용하면 메모리상 내부 데이터베이스를 생성하고, @Entity 클래스를 등록한 후 JPA Repository 설정들을 해준다. 각 테스트마다 테스트가 완료되면 관련한 설정들은 롤백된다.
//test 하위에 application.yml 설정
runtimeOnly 'com.h2database:h2'
5. Test Container 활용 테스트
테스트를 위해서는 운영과 동일한 형태의 개발 환경에서 테스트하는 것이 중요하다. 하지만, DataJpaTest는 H2 인메모리 DB로 테스트하기 때문에 운영 DB와 여러 설정이 다를 수 있다.
그래서, Docker를 이용해서 테스트마다 테스트를 위한 컨테이너를 실행시켜서 테스트하고, 컨테이너를 제거해주면 좋은데 그런 기능을 TestContainer를 이용해서 가능하다.
package com.wedul.javajunit5studyjunit.docker;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import javax.transaction.Transactional;
import static org.assertj.core.api.Assertions.*;
@ActiveProfiles("test")
@SpringBootTest
@Testcontainers
class StudentServiceTest {
@Autowired
StudentService studentService;
@Container
static MySQLContainer mariaDBContainer = new MySQLContainer();
@Test
@DisplayName("학생 추가하기")
@Transactional
void create_student_test() {
studentService.createStudent(Student.builder()
.studentId(2L)
.age(10)
.name("wedul")
.address("seoul jamsil")
.build()
);
}
@DisplayName("student 조회 테스트")
@Test
@Transactional
void find_student_test() {
studentService.createStudent(Student.builder()
.studentId(2L)
.age(10)
.name("wedul")
.address("seoul jamsil")
.studentNickname("duri")
.build()
);
Student student = studentService.getStudent(2L);
assertThat(student).isNotNull();
}
}
관련 설정은 아래 블로그를 참고하자.