Junit

Java 프로그래밍 언어를 위한 가장 널리 사용되는 단위 테스트(Unit Testing) 프레임워크.
소프트웨어 개발 과정에서 코드의 품질을 보장하고 버그를 사전에 발견하는 데 중요한 역할을 한다.

JUnit은 Kent Beck과 Erich Gamma에 의해 1997년에 처음 개발되었.
당시 소프트웨어 개발에서 테스트의 중요성이 점점 커지면서, 개발자들이 쉽게 사용할 수 있는 테스트 프레임워크의 필요성이 대두되었.
현재는 JUnit 5 버전까지 발전했으며, 각 버전마다 더욱 강력하고 사용하기 쉬운 기능들이 추가되었다.

Spring Boot 2.2.0 버전부터는 기본적으로 JUnit 5를 지원한다.

JUnit의 핵심 개념

  1. 단위 테스트 (Unit Test)

    • 소프트웨어의 가장 작은 단위(일반적으로 메서드나 함수)를 독립적으로 테스트하는 방법
    • 개별 컴포넌트가 예상대로 작동하는지 확인
  2. 주요 어노테이션

    • @Test: 테스트 메서드임을 선언
    • @BeforeEach: 각 테스트 메서드 실행 전에 실행
    • @AfterEach: 각 테스트 메서드 실행 후에 실행
    • @BeforeAll: 테스트 클래스의 모든 테스트 실행 전 한 번만 실행
    • @AfterAll: 테스트 클래스의 모든 테스트 실행 후 한 번만 실행

테스트 작성 방법

  1. Given-When-Then 패턴을 사용하여 테스트를 구조화한다.
  2. @BeforeEach, @AfterEach 어노테이션을 사용하여 각 테스트 전후에 실행될 코드를 정의한다.
  3. @BeforeAll, @AfterAll 어노테이션을 사용하여 전체 테스트 클래스의 시작과 끝에 실행될 코드를 정의한다.

주요 어노테이션

  1. @SpringBootTest: 전체 애플리케이션 컨텍스트를 로드하여 통합 테스트를 수행한다.
  2. @ExtendWith(SpringExtension.class): JUnit 5에서 Spring TestContext Framework를 통합한다.
  3. @WebMvcTest: 웹 계층 테스트에 사용된다.
  4. @DataJpaTest: JPA 관련 테스트 구성을 자동으로 설정한다.
  5. @MockBean: Spring Boot 컨테이너에 Mock 객체를 추가한다.

Spring Test 특징

  1. ApplicationContext 관리: @RunWith(SpringRunner.class)와 @ContextConfiguration을 사용하여 Spring의 ApplicationContext를 테스트에서 사용할 수 있다.
  2. 트랜잭션 관리: @Transactional 어노테이션을 사용하여 테스트 후 데이터베이스 롤백을 자동화할 수 있다.
  3. 목 객체 사용: Mockito와 통합하여 의존성을 모의 객체로 대체할 수 있다.

JUnit 5의 아키텍처

JUnit 5는 세 개의 주요 모듈로 구성된다:

  1. JUnit Platform: 테스트 엔진을 위한 기본 플랫폼
  2. JUnit Jupiter: 최신 테스트 작성을 위한 프로그래밍 모델과 확장 모델
  3. JUnit Vintage: 이전 버전(JUnit 4)의 테스트를 실행하기 위한 엔진

Spring에서의 JUnit 설정

  1. 의존성 추가:
    Maven 프로젝트의 경우, pom.xml에 다음 의존성을 추가한다:

    1
    2
    3
    4
    5
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
  2. 테스트 클래스 생성:
    src/test/java 디렉토리에 테스트 클래스를 생성한다다

실제 JUnit 사용 예시

 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
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;

class CalculatorTest {
    private Calculator calculator;

    // 각 테스트 전에 Calculator 인스턴스 초기화
    @BeforeEach
    void setUp() {
        calculator = new Calculator();
    }

    // 덧셈 테스트
    @Test
    void testAddition() {
        // given: 테스트에 필요한 데이터 준비
        int a = 10;
        int b = 20;

        // when: 실제 테스트할 메서드 호출
        int result = calculator.add(a, b);

        // then: 결과 검증
        assertEquals(30, result, "10 + 20은 30이어야 합니다");
    }

    // 예외 테스트
    @Test
    void testDivisionByZero() {
        // 0으로 나누기 시 예외가 발생하는지 확인
        assertThrows(ArithmeticException.class, () -> {
            calculator.divide(10, 0);
        });
    }
}

주요 단언(Assertion) 메서드

  • assertEquals(expected, actual): 두 값이 같은지 확인
  • assertTrue(condition): 조건이 참인지 확인
  • assertFalse(condition): 조건이 거짓인지 확인
  • assertNull(object): 객체가 null인지 확인
  • assertNotNull(object): 객체가 null이 아닌지 확인
  • assertThrows(): 특정 예외가 발생하는지 확인

테스트의 장점

  1. 코드 품질 향상: 버그를 조기에 발견
  2. 리팩토링 용이성: 기존 코드를 안전하게 수정 가능
  3. 문서화 기능: 코드의 동작을 실행 가능한 문서로 제공
  4. 설계 개선: 테스트 작성 과정에서 코드의 결합도를 낮추고 모듈성 향상

학습 로드맵

  1. Java 기본 문법 숙달
  2. 객체지향 프로그래밍 개념 이해
  3. JUnit 기본 어노테이션과 assertion 메서드 학습
  4. 간단한 단위 테스트 작성 연습
  5. 실제 프로젝트에 테스트 코드 적용

고급 기능

  • 매개변수화된 테스트
  • 중첩된 테스트 클래스
  • 동적 테스트 생성
  • 테스트 인스턴스 라이프사이클 제어

주의사항

  • 모든 것을 테스트할 필요는 없음
  • 의미 있고 중요한 부분에 집중
  • 테스트 코드도 유지보수 가능한 깨끗한 코드여야 함

참고 및 출처

JUnit 5