Coding/Server

[내일배움캠프] 테스트코드 - 실습

kangplay 2025. 4. 17. 17:21

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 을 사용하고, 만약 롤백할 필요가 없는 테스트라면 따로 트랜잭션 선언이 불필요하다.

https://www.inflearn.com/community/questions/395376/%EC%9D%B4%EC%A4%91-transactional-%EC%A7%88%EB%AC%B8-%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4?srsltid=AfmBOookYY4j89fUJFK7HDVnnDkB_Ub-sXrdvFRLGuxz5_4ZtJ6IfLV6

 

이중 @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();
    }

}

관련 설정은 아래 블로그를 참고하자.

https://hogwart-scholars.tistory.com/entry/Spring-Boot-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%A1%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%98%EA%B8%B0