테스트 코드 (통합 테스트) ( 스탠다드 )

2025. 5. 14. 19:45·[내일배움캠프-Sparta]/Spring 6기
반응형

📅 통합 테스트

전에 나왔던 단위 테스트의 3가지 요구사항 중 하나라도 충족하지 못하는 테스트가 통합 테스트에 해당됨.

  1.  작은 코드 조각 (단위)을 검증하고,
  2.  빠르게 수행되어야 하며,  // 외부 의존성을 주입하는 테스트
  3. 격리된 방식이 아닌 테스트 // 외부 의존성을 주입하는 테스트

외부 의존성으로부터 격리 ➡ 단위 테스트

외부 의존성과 함께 ➡ 통합 테스트

의존성의 두가지 유형

더보기
  1. 관리 의존성 (전체를 제어할 수 있는 의존성) : 해당 의존성과의 상호 작용을 외부 환경에서 볼 수 없다.                            ➡ 데이터베이스, Redis, RabbitMQ 등
  2. 비관리 의존성 (전체를 제어할 수 는 없는 의존성) : 해당 의존성과의 상호 작용을 외부에서 볼 수 있다.                            ➡ SMTP 서버, 메시지 버스, 카카오페이, 네이버 로그인, 카카오 로그인

통합 테스트에서 Mock 처리해야 하는것은 비관리 의존성, 관리 의존성은 실제 인스턴스를 사용해야 한다

 

하지만 DB의 경우, 실제 인스턴스를 사용해 테스트하기 때문에 기존에 사용하던 데이터가 오염될수 있어 테스트용 DB 환경을 따로 구축해주어야 한다!!

✅ Test용 DB 세팅법

1️⃣ Local 환경에 DB를 직접 설치해서 사용

아주 심플한 방법이며, Mac유저라면 homebrew를 이용하거나, dmg 파일을 공식 홈페이지에서 다운받아 아주 쉽게 로컬DB를 설치할 수 있습니다.

  • 장점
    • 초기 세팅이 간단
    • 한번 설치하면, 이후 동일 DBMS를 사용하는 다른 프로젝트에도 활용할 수 있어 꽤 편리하다.
  • 단점
    • 개발자마다 DB에 다른 데이터가 저장되어 있을 수 있으니, 멱등성 유지를 위해 테스트 실행 전에 과정이 필수로 요구됨.
    • 프로젝트 마다 sql level에서 직접 새로운 계정, schema를 만드는 작업이 필요하다.

2️⃣ Docker container에 DB를 올려 사용

일반적으로 컨테이너에 DB를 올리지 않습니다. 

➡ 컨테이너를 삭제하면 DB에 내용도 전부 삭제되며, 컨테이너 대수에 따라 동기화 문제가 발생할 수도 있어서 위험하기 때문

하지만 로컬에서 기능의 동작 여부만 확인하기 위해 더미 데이터를 넣고 테스트용으로 사용한다면, 앞서 말한 문제를 크게 신경 쓸필요가 없어진다.

  • 장점
    • 한번 환경 설정을 마치면, 이후 건드릴 필요가 없다.
  • 단점
    • 이전에 도커를 써본적이 없다면 docker학습, 세팅, dockerfile 작성 등의추가 공수가 꽤 드는편이다.
    • 테스트를 위해 도커를 실행하는 과정이 필요하고, 만약 DDL이 수행되어야 하면, DBMS에 따라 DB cache를 비워줘야 하는 번거로운 작업이 있다.

3️⃣ H2 DB사용

H2는 Java기반의 In-memory RDBMS로 따로 설치할 필요 없이 의존성만 추가해주면 되며, 표준 SQL을 대부분 지원해주며, 용량도 약 2MB로 매우 가벼워 테스트용도로 사용하기 적절하다.

  • 장점
    • 설치할 필요가 없다.
    • 인메모리 기반이기 때문에 빠르다
    • 브라우저 콘솔을 지원
  • 단점
    • 운영 DB에 필요한 문버을 100% 지원해주는 것이 아니다. 따라서 특정 DB에 특화된 기능을 테스트하기 어렵다.

🤔 전부 단점이 하나씩은 있는데요?

위에서 언급한 방법들의 단점을 요약하면 다음과 같습니다.

  1. 러닝 커브가 있다
  2. 사전 작업이 번거롭다
  3. 멱등성 유지가 안된다
  4. DB 특화 기능을 소화하지 못한다.

이 중에서 2, 3번 단점이 가장 크리티컬합니다.

테스트는 정말 숨 쉬듯 돌려볼 수 있어야하는데,

테스트를 돌리는 과정이 귀찮다면 자연스럽게 테스트 작성을 생략하게 되고,

테스트하는 사람마다 다른 테스트 결과를 받는다면, 그 테스트 결과를 신뢰할 수 없기 때문입니다.

따라서 프로젝트에 적용될 테스트 환경은 아래와 같은 조건들을 충족해야 합니다.

1. 테스트하기 편해야 한다

: 시간이 적게 들고(빠른 피드백), 가벼우며, 명령어 하나로도 충분한 그런 방법

2. 멱등성을 유지할 수 있어야 한다

→ 어디서 실행하든, 어떤 컴퓨터에서 실행하든 동일한 테스트 결과를 제공해야 합니다.

3. DB 특화 기능을 소화할 수 있어야 한다

: 특정 DBMS에 종속적인 기능이라면, 해당 기능이 지원되지 않을 경우 프로젝트에 큰 영향을 미칠 수 있습니다

4️⃣ TestContainer 

Testcontainers는 docker 컨테이너를 외부 설정 없이 Java 언어만으로 구축할 수 있는 오픈소스 라이브러리입니다.

이를 사용하게 되면 단순 테스트 실행만으로도 mysql 컨테이너가 실행되고 스프링 테스트는 해당 컨테이너에

등록된 DB로 테스트를 하게 되어 정말 쉽게 어떤 환경에서도 멱등성있는 테스트 환경을 구축할 수 있습니다.

지금부터 예제를 통해 TestContainers로 테스트 환경을 구축해보겠습니다 .

 

// 의존성 추가
testImplementation 'org.testcontainers:testcontainers:1.18.3'  // TC 의존성
testImplementation 'org.testcontainers:mysql:1.19.3'  // MySQL 테스트 컨테이너 사용
testImplementation 'org.testcontainers:junit-jupiter:1.16.3'  // TC 의존성
testImplementation 'com.mysql:mysql-connector-j:8.2.0'

✅ 통합 테스트 코드 작성

손님이 상품을 구매하였을 때, 상품의 재고에 대한 동시성 이슈 발생을 막기 위한 통합 테스트 코드를 아래와 같이 작성

@ActiveProfiles("test")    // 1
@SpringBootTest            // 2
@Sql("classpath:/init_table.sql")   // 3
@Sql("classpath:/dml.sql")          // 4
public class ProductServiceTest {

    @Autowired                      // 5
    ProductService productService;
    @Autowired
    ProductRepository productRepository;

		@Test
    void 성공_동시성_이슈_테스트() throws InterruptedException {
        // given
        List<OrderLineRequest> orderLines = List.of(
            new OrderLineRequest(1L, 10L),
            new OrderLineRequest(2L, 20L)
        );
        OrderCreateRequest request = new OrderCreateRequest(orderLines, 10000L);
        int numberOfThreads = 4;
        ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
        CountDownLatch countDownLatch = new CountDownLatch(4);
        when(mailPlugin.send()).thenReturn(true);

        // when
        executorService.execute(() -> {
            orderService.create(request);
            countDownLatch.countDown();
        });
        executorService.execute(() -> {
            orderService.create(request);
            countDownLatch.countDown();
        });
        executorService.execute(() -> {
            orderService.create(request);
            countDownLatch.countDown();
        });
        executorService.execute(() -> {
            orderService.create(request);
            countDownLatch.countDown();
        });
        countDownLatch.await();

        // then
        Product product1 = productRepository.findById(1L).orElseThrow();
        Product product2 = productRepository.findById(2L).orElseThrow();

        assertThat(product1.getAmount()).isEqualTo(9960L);
        assertThat(product1.getSaleCount()).isEqualTo(40L);
        assertThat(product2.getAmount()).isEqualTo(19920L);
        assertThat(product2.getSaleCount()).isEqualTo(80L);
    }    
}
  1. @ActiveProfies("test")
    • TestContainers 에서 만들 MySqlContainer 의 dataSource를 방금 작성한 application-test.yml 의값으로 설정하기 위한 어노테이션
  2. @SpringBootTest
    • Service ~ Repository 까지의 로직을 목 처리없이 다루려면 의존성 주입을 해주어야 하는데, 그를 위해 붙여주는 어노테이션
  3. @Sql
    • 쿼리문이 적혀있는 sql file의 경로를 적어주면, 테스트 시작 전에 이를 수행해줌
  4. @Autowired
    • 의존성을 Mock처리하지 않고, 실제 인스턴스를 사용하기 위해 해당 어노테이션으로 Bean 주입을 해줌.
  5. 동시성 테스틀 위한 쓰레드 4개를 생성해 각 쓰레드가 동시에 구매를 진행함.
    • 동시성 이슈가 발생했다면 상품이 4개보다 조금더 깎이겠죠?

✅ 테스트 결과 확인

1. @Sql 어노테이션을 이용한 자동 테이블 생성

2. 테스트 시작 시 자동으로 생성된 Mysql Docker Container

3. 생성된 컨테이너들은 테스트가 끝나면 자동으로 삭제

TestContainers 의 코어인 Ryuk Container 가 테스트 끝에서 정지 작업을 처리해주기 때문

지금까지 TestContainers 를 이용하여 간단하게 통합 테스트를 위한 DB Test 환경을 구축해봤습니다.

 

< 현재 >

테스트 메서드가 여러개일 경우, 각 메서드마다 @Sql 어노테이션으로 인한 테이블 재생성이 일어나고,

통합 테스트 클래스가 여러개라면 클래스가 각 클래스마다 TestContainer로 인해 MySql Container 가 매번 생

성됩니다.

이러한 현상으로 인해 메모리가 낭비되고, 테스트 시간 또한 오래 소요되게 됩니다.

그렇기 때문에 테스트 로직을 통해 생성되는 데이터들이 서로 침범되지 않는다면, MySql 싱글톤 컨테이너를 생성

하는 것이 이상적입니다.

💡 참고

테스트 커버리지 ➡ 모든 경우를 테스트 하는 것이 아니다.

모든 함수들에 대해 테스트 코드가 필요한 것은 아니다. 단순 생성의 경우는 테스트를 할 필요가 없을수 도 있고, 테스트 커버리지를 높일 수 있으면 좋다. 하지만, 100%의 테스트 커버리지를 가지는 것이 좋은 설계가 되는 것인지는 의문이 든다.

 

https://techblog.woowahan.com/2613/

 

테스트 코드 없이 레거시 코드를 다 감수하시겠습니까? | 우아한형제들 기술블로그

부서 이동을 하다 2018년 말미, 결제/정산 파트에서 주문중계 파트로 부서 이동하게 되었습니다. 인사 발령을 받고 나서 팀 이동을 하게 되면 누구나 직면하게 되는 상황이 발생하는데요. 그것은

techblog.woowahan.com

 

 

 

반응형

'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글

TIL 57 [ AWS( EC2, ELB, RDS ) ]  (0) 2025.05.15
TIL 56 [ 통합 테스트 + JPA 심화 플러스 과제 ( Lv 3-1 ) ]  (0) 2025.05.14
TIL 55 [ 커리어 데이 ( 커리어여정, 협업 가이드, 면접 팁, 취업 ) ]  (0) 2025.05.13
TIL 54 [ Redis 공부 + 커리어데이( 이력서, 개발 성장기 ,AI 시대 신입 개발자 ) ]  (0) 2025.05.12
Redis 기초 ( 챌린지반 )  (0) 2025.05.11
'[내일배움캠프-Sparta]/Spring 6기' 카테고리의 다른 글
  • TIL 57 [ AWS( EC2, ELB, RDS ) ]
  • TIL 56 [ 통합 테스트 + JPA 심화 플러스 과제 ( Lv 3-1 ) ]
  • TIL 55 [ 커리어 데이 ( 커리어여정, 협업 가이드, 면접 팁, 취업 ) ]
  • TIL 54 [ Redis 공부 + 커리어데이( 이력서, 개발 성장기 ,AI 시대 신입 개발자 ) ]
dimenshun
dimenshun
한 소년의 개발 일기
    반응형
  • dimenshun
    Dev Life Notes
    dimenshun
  • 전체
    오늘
    어제
    • 분류 전체보기 (268)
      • CS (23)
        • 자료구조 (0)
        • 알고리즘 (0)
        • 컴퓨터 구조 (8)
        • 네트워크 (6)
        • 운영체제 (3)
        • DB ( + SQLD ) (5)
        • SW공학 (1)
      • 프로그래밍 (3)
        • Java (0)
        • Spring (0)
        • HTML,CSS (3)
        • JavaScript (0)
      • 개발 툴 (7)
        • Git(버전관리) (1)
        • Docker (3)
        • AWS (2)
        • JSP (1)
      • 코딩테스트(Algorithm) (125)
        • 백준 (6)
        • 프로그래머스 (119)
      • [내일배움캠프-Sparta] (110)
        • Spring 6기 (106)
        • KPT 회고 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Python
    spring
    세션
    개발자
    SQLD
    네트워크
    코딩테스트
    컴퓨터구조
    Til
    OS
    cs
    메모리
    내일배움캠프
    db
    Java
    백엔드
    docker
    SQL
    AWS
    CPU
    트랜잭션
    KPT
    It
    웹
    network
    배포
    운영체제
    web
    Testcode
    알고리즘
  • hELLO· Designed By정상우.v4.10.3
dimenshun
테스트 코드 (통합 테스트) ( 스탠다드 )
상단으로

티스토리툴바