Spring Boot Junit5 이해하기 - 환경 구성 및 활용 예제

2025. 6. 10. 09:42spring/TDD

해당 글에서는 이전에 습득한 이론을 바탕으로 Spring Boot JUnit을 다양한 예제를 통해서 익혀보자.

Spring Boot 2.2 + 이상 버전부터는 기본적으로 의존성이 추가되어 있기에 별도의 세팅은 하지 않는다.

클래스/ 테스트 전후 수행 테스트 : @BeforeAll, @AfterAll, @BeforeEach, @AfterEach

테스트 수행 구조

  • @BeforeAll, @AfterAll 어노테이션을 이용하여 테스트 클래스의 시작/후에 단 한번 호출
  • @BeforeEach, @AfterEach 어노테이션을 이용하여 매 @Test 전후에 호출

어노테이션 설명

@BeforeAll JUnit 테스트 클래스 내에서 모든 테스트 메소드 실행 전에 한 번 실행되는 메소드를 정의하는 데 사용됩니다. 보통 테스트 환경의 설정이나 초기화에 사용됩니다.
@AfterAll JUnit 테스트 클래스 내에서 모든 테스트 메소드 실행 후에 한 번 실행되는 메소드를 정의하는 데 사용됩니다. 주로 사용한 리소스를 정리하거나 테스트 환경을 종료하는 데 사용됩니다.
@BeforeEach JUnit 테스트 클래스 내의 각 테스트 메소드마다 실행되는 메소드를 정의하는 데 사용됩니다. 주로 각 테스트의 준비 작업이나 초기화에 사용됩니다.
@AfterEach JUnit 테스트 클래스 내의 각 테스트 메소드 실행 후에 실행되는 메소드를 정의하는 데 사용됩니다. 주로 각 테스트의 후처리 작업이나 정리 작업에 사용됩니다.
package com.adjh.multiflexapi;

import com.adjh.multiflexapi.model.CodeDto;
import com.adjh.multiflexapi.service.CodeService;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;

class MultiFlexApiApplicationTests {

    @Test
    @DisplayName("두 숫자의 합 테스트")
    void testEqualsSum() {
        System.out.println("[+] @test assertEquals Annotation");
        int a = 5;
        int b = 7;
        int expectedSum = 12;
        int actualSum = a + b;
        Assertions.assertEquals(expectedSum, actualSum);
    }

    @Test
    @DisplayName("Object NULL 여부 테스트")
    void testIsNotNull() {
        Object obj = null;
        Assertions.assertNull(obj);
        System.out.println("[+] @test assertNull Annotation");
    }

    // 각 테스트 메소드 실행 전에 실행되는 메소드를 정의하는 어노테이션
    @BeforeEach
    void setUp() {
        System.out.println("@BeforeEach Annotation");
    }

    // 각 테스트 메소드 실행 후에 실행되는 메소드를 정의하는 어노테이션
    @AfterEach
    void cleanUp() {
        System.out.println("@AfterEach Annotation");
    }

    // 클래스 내의 모든 테스트 메서드 실행 전에 한 번 호출되는 메서드
    @BeforeAll
    static void setUpBeforeClass() {
        System.out.println("@BeforeAll Annotation");
    }

    // 클래스 내의 모든 테스트 메서드 실행 후에 한 번 호출되는 메서드
    @AfterAll
    static void cleanUpAfterClass() {
        System.out.println("@AfterAll Annotation");
    }

}

  1. 테스트를 시작하면 @BeforeAll 어노테이션을 통해 setUpBeforeClass() 함수가 수행됩니다.
  2. @Test 어노테이션을 통해 수행되기 이전에 @BeforEach의 setUp() 함수가 수행됩니다.
  3. @Test 어노테이션을 통해 testEqualsSum() 함수가 수행됩니다.
  4. @Test 어노테이션을 통해 수행된 후 @AfterEach의 cleanUp() 함수가 수행됩니다.
  5. 동일한 과정으로 @Test 어노테이션을 통해 수행되기 이전에 @BeforEach의 setUp() 함수가 수행됩니다.
  6. 동일한 과정으로 @Test 어노테이션을 통해 testIsNotNull() 함수가 수행됩니다.
  7. 동일한 과정으로 @Test 어노테이션을 통해 수행된 후 @AfterEach의 cleanUp() 함수가 수행됩니다.
  8. 최종 테스트 종료 이전에 @AfterAll 어노테이션을 통해 cleanUpAfterClass() 함수가 수행됩니다.


Given-When-Then 패턴 시나리오 테스트

섹션 설명

Given : 설정 테스트의 초기 상태 또는 사전 조건을 설정합니다. 입력 데이터나 테스트가 실행될 문맥을 지정합니다.
When : 동작 테스트되는 동작 또는 이벤트를 설명합니다. 테스트되는 특정 메서드나 동작을 나타냅니다.
Then : 검증 "When" 섹션에서 설명한 동작으로 인해 기대되는 결과 또는 동작을 정의합니다.
package com.adjh.multiflexapi;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

class CalculatorTests {

    @BeforeAll
    static void setUpBeforeAll() {
        System.out.println("@BeforeAll Annotation");
    }

    @Test
    void testAddingTwoNumbers() {
        // given : 초기 상태 지정
        int a = 2;
        int b = 3;

        // when : 테스트 동작 정의
        int result = Calculator.add(a, b);

        // then : 검증
        int expected = 5;
        Assertions.assertEquals(expected, result);
        System.out.println("Test for adding two numbers passed");
    }

    @Test
    void testDividingTwoNumbers() {
        // given : 초기 상태 지정
        int a = 10;
        int b = 2;

        // when : 테스트 동작 정의
        int result = Calculator.divide(a, b);

        // then : 검증
        int expected = 5;
        Assertions.assertEquals(expected, result);
        System.out.println("Test for dividing two numbers passed");
    }

    @AfterAll
    static void cleanUpAfterAll() {
        System.out.println("@AfterAll Annotation");
    }
}

class Calculator {
    static int add(int a, int b) {
        return a + b;
    }

    static int divide(int a, int b) {
        return a / b;
    }
}

 

  1. 테스트를 시작하면 @BeforeAll 어노테이션을 통해 setUpBeforeClass() 함수가 수행됩니다.
  2. @Test 어노테이션을 통해 testAddingTwoNumbers() 함수가 수행됩니다.
  3. @Test 어노테이션을 통해 testDividingTwoNumbers() 함수가 수행됩니다.
  4. 테스트를 종료하면서 @AfterAll 어노테이션을 통해 cleanUpAfterClass() 함수가 수행됩니다.


💡 중첩된 테스트

  • 클래스 내에서 테스트가 많이 있어서 가독성이 떨어지는 경우 클래스 내에 클래스를 구현하여서 @Nested 어노테이션을 통해서 테스트 별로 묶어서 수행하는 방법
  • 이를 통해 테스트들을 구조화하여 가독성을 향상

💡 @Nested

  • JUnit에서 중첩된 테스트 클래스를 작성하는 데 사용
  • 중첩된 테스트 클래스는 특정 테스트 그룹을 더 잘 구조화하고 테스트 코드의 가독성을 향상
  • 중첩된 테스트 클래스는 내부 클래스로 선언되며, 외부 테스트 클래스의 인스턴스와 상호작용 가능
  • 이를 통해 외부 테스트 클래스의 상태를 공유하거나 테스트 케이스 간의 의존성을 관리
package com.adjh.multiflexapi;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

class NestedTests {

    @Nested
    class CalcTests {
        @Test
        @DisplayName("두 숫자의 합 테스트")
        void testSum() {
            int a = 5;
            int b = 7;
            int expectedSum = 12;
            int actualSum = a + b;
            Assertions.assertEquals(expectedSum, actualSum);
        }

        @DisplayName("두 숫자의 합 테스트")
        @Test
        void testAddingTwoNumbers() {
            // given : 초기 상태 지정
            int a = 2;
            int b = 3;

            // when : 테스트 동작 정의
            int result = Calculator.add(a, b);

            // then : 검증
            int expected = 5;
            Assertions.assertEquals(expected, result);
            System.out.println("Test for adding two numbers passed");
        }

        @Test
        @DisplayName("두 수의 값 나누기 ")
        void testDividingTwoNumbers() {
            // given : 초기 상태 지정
            int a = 10;
            int b = 2;

            // when : 테스트 동작 정의
            int result = Calculator.divide(a, b);

            // then : 검증
            int expected = 5;
            Assertions.assertEquals(expected, result);
            System.out.println("Test for dividing two numbers passed");
        }
    }

    @Nested
    class IsNotNullTest {
        @Test
        @DisplayName("Object NULL 여부 테스트")
        void testIsNotNull() {
            Object obj = null;
            Assertions.assertNull(obj);
            System.out.println("[+] @test assertNull Annotation");
        }

    }

}

 

  • @Nested 어노테이션이나 @Test 어노테이션은 순서를 보장하지 않기에 구성한 예시로 확인
  1. 테스트를 시작하면 @Nested 어노테이션을 통해 CalcTests Class가 수행이 됩니다.
  2. CalcTests 클래스 내에서 @Test 어노테이션을 확인하여 testSum() 메서드가 수행이 됩니다.
  3. CalcTests 클래스 내에서 @Test 어노테이션을 확인하여 testAddingTwoNumbers() 메서드가 수행이 됩니다.
  4. CalcTests 클래스 내에서 @Test 어노테이션을 확인하여 testDividingTwoNumbers() 메서드가 수행이 됩니다.
  5. 다음으로 @Nested 어노테이션을 통해 IsNotNullTests Class가 수행이 됩니다.
  6. IsNotNullTests 클래스 내에서 testsNotNull() 메서드가 수행됩니다.
  7. 테스트가 종료됩니다.


@SpringBootTest 어노테이션을 사용하여 테스트

  • @SpringBootTest 를 사용하면 애플리케이션 컨텍스트를 로드하고 필요한 빈을 초기화하는 등 테스트에 필요한 설정과 의존성을 설정하는데 도움이 됨
  • 이를 통해 실제 환경과 유사한 설정에서 테스트를 수행하고 각각의 컴포넌트가 상호작용하는 방식을 확인 가능
  • 애플리케이션의 다양한 컴포넌트를 테스트할 수 있으며, 외부 서비스와의 상호작용, 데이터베이스 연동 등을 포함한 실제 환경에서의 동작을 확인 가능
  • @SpringBootTest 를 선언하면 @Test 를 수행하면서 아래와 같이 로컬 서버가 수행되면서 테스트가 진행되는 것을 확인 가능

package com.adjh.multiflexapi;

import com.adjh.multiflexapi.model.CodeDto;
import com.adjh.multiflexapi.service.CodeService;
import org.junit.jupiter.api.Assertions;
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;

@SpringBootTest
class ServiceTests {
    @Autowired
    private CodeService codeService;

    @Test
    @DisplayName("코드리스트 테스트 ")
    void codeList() {
        System.out.println("[+] 코드리스트를 조회합니다.");

        // Given
        CodeDto codeDto = CodeDto.builder().cd("java").build();

        // When
        CodeDto resultDto = codeService.selectCode(codeDto);

        // Then
        Assertions.assertNotNull(resultDto);
    }
}

  1. 클래스 내에 @SpringBootTest 어노테이션을 선언하였습니다. (*이는 @Test 어노테이션 수행직전에 수행이 됩니다)
  2. 테스트를 시작하면 @BeforeAll 어노테이션을 통해 setUpBeforeClass() 함수가 수행됩니다.
  3. @Test 어노테이션이 수행되기 직전에 로컬 서버가 실행이 되어 각각의 컨텍스트를 로드해 옵니다.
  4. @Test 어노테이션을 통해 testIsNotNullCodeList() 함수가 수행됩니다.
  5. 테스트를 종료하면서 @AfterAll 어노테이션을 통해 cleanUpAfterClass() 함수가 수행됩니다.