[Java] Junit

gyeol·2025년 9월 24일

자바

목록 보기
16/24
post-thumbnail

Junit?

Junit은 Java에서 독립된 단위테스트를 지원해주는 프레임워크이다.

단위 테스트? (Unit Test)
특정 소스코드의 모듈이 의도한대로 잘 작동하는지 검증하기 위함
Spring에서 단위 테스트는 Spring Container에 올라와있는 Bean을 테스트하는 것

왜 사용하는데 ?

  • 코드의 품질을 향상시켜 코드 자체의 신뢰성을 높이고 오류 조기 발견 위함
  • 리팩토링과 기능을 추가하는 과정에서 코드의 안정성 유지시킬 수 있음
  • 기능의 요구사항과 동작 방식을 명확히 설명할 수 있음

특징

  • Annotations 기반
    @Test, @BeforeEach 와 같은 직관적인 애노테이션을 사용해 테스트의 구조와 생명주기 관리 가능
  • Assertions(단언)
    assertEquals(),assertTrue(), assertNotNull()과 같은 단언 메서드를 제공해 테스트 결과가 예상과 일치하는지 확인 가능
  • 독립적인 실행
  • 오픈 소스
  • 통합 용이성
    Maven, Gradle과 같은 빌드 도구 및 Eclipse, IntelliJ와 같은 IDE와 쉽게 통합해 테스트 자동화 가능

주요 애노테이션

애노테이션설명
@Test이 메서드가 테스트 케이스임을 나타냄
→ 테케 맨 위에 명시 필수
@DisplayName(”설명”)테스트 클래스나 메서드에 대한 설명을 보기 쉽게 저장
@AfterEach각각의 @Test 실행된 후 실행되어야 함
@BeforeEach각각의 @Test 실행 전 먼저 실행되어야 함
@AfterAll현재 클래스의 모든 @Test 메서드가 실행된 후 마지막으로 한 번만 실행
@BeforeAll현재 클래스의 모든 @Test 메서드 중 최초로 한 번만 실행
@Disabled특정 테스트를 일시적으로 비활성화할 때 사용
@Nested관련있는 테스트들을 하나의 중첩 클래스로 묶어 계층 구조로 만들 때 사용
다른 클래스 안에서 또 다른 테스트 가능

라이프사이클

크게 클래스 레벨과 메서드 레벨로 나뉜다.

  1. 클래스 레벨 설정 (1번만 실행)

    • @BeforeAll을 사용해 클래스 전체에서 딱 한번 실행
    • 꼭 static 메서드여야 함
      ex) DB 연결, 네트워크 소켓 열기 등
  2. 테스트 메서드 레벨 (각 테스트마다 반복)

    • @BeforeEach
      @Test가 실행되기 직전에 항상 호출됨
      ex) 테스트에 사용할 객체 생성, 테스트 환경 초기화하여 각 테스트가 서로 영향 주지 않도록 독립성 보장
    • @Test
      실제 테스트 코드 실행. Assertions를 통해 코드 동작 검증
    • @AfterEach
      @Test 메서드가 실행된 직후에 항상 호출 (테스트 성공/실패 여부 관계없이 실행됨)
      ex) @BeforeEach에서 사용한 자원이나 데이터를 정리해 다음 테스츠에 영향을 주지 않도록
  3. 클래스 레벨 정리 (1번만 실행)

    • @AfterAll을 사용해 모든 테스트 완전히 종류된 후 마지막에 딱 한번 실행.
    • 꼭 static 메서드여야 함
      ex) @BeforeAll에서 설정한 DB 연결 등 해제

사용 방법

  1. 프로젝트 생성 (Gradle or Maven)
    pom.xml or build.gradle 파일에 의존성 주입 → refresh 필수
<dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.10.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.10.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

2. 테스트할 클래스 작성 (src/main 폴더에)
간단히 이메일과 나이를 검증하는 로직

package org.example;

public class MemberValidator {

    /**
     * 이메일 형식이 유효한지 검증합니다. (간단한 '@' 포함 여부만 체크)
     * @param email 검증할 이메일
     * @return 유효하면 true, 아니면 false
     */
    public boolean isValidEmail(String email) {
        if (email == null) {
            return false;
        }
        return email.contains("@");
    }

    /**
     * 성인인지 검증합니다. (19세 이상)
     * @param age 검증할 나이
     * @return 성인이면 true, 아니면 false
     */
    public boolean isAdult(int age) {
        return age >= 19;
    }

    /**
     * 유효하지 않은 이메일일 경우 예외를 발생시킵니다.
     * @param email 검증할 이메일
     */
    public void validateEmailOrThrow(String email) {
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("유효하지 않은 이메일 형식입니다.");
        }
    }
}
  1. 테스트 코드 작성 (꼭 test/java 밑에 넣기)
import org.example.MemberValidator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

@DisplayName("회원 검증 로직 테스트")
class MemberValidatorTest {

    private MemberValidator validator;

    // 각 테스트가 실행되기 전에 MemberValidator 객체를 새로 생성합니다.
    @BeforeEach
    void setUp() {
        validator = new MemberValidator();
        System.out.println("새 Validator 객체 생성 완료");
    }

    @Test
    @DisplayName("이메일에 '@'가 포함되면 성공한다")
    void email_contains_at_sign_should_be_valid() {
        // given (준비)
        String email = "test@example.com";

        // when (실행)
        boolean result = validator.isValidEmail(email);

        // then (검증)
        assertTrue(result, "유효한 이메일은 true를 반환해야 합니다.");
    }

    @Test
    @DisplayName("이메일에 '@'가 없으면 실패한다")
    void email_without_at_sign_should_be_invalid() {
        // given
        String email = "testexample.com";

        // when
        boolean result = validator.isValidEmail(email);

        // then
        assertFalse(result, "유효하지 않은 이메일은 false를 반환해야 합니다.");
    }

    @Test
    @DisplayName("나이가 19세 이상이면 성인으로 판단한다")
    void age_19_or_more_should_be_adult() {
        // given
        int age = 20;

        // when
        boolean result = validator.isAdult(age);

        // then
        assertTrue(result);
    }

    @Test
    @DisplayName("나이가 19세 미만이면 성인이 아니라고 판단한다")
    void age_less_than_19_should_not_be_adult() {
        assertFalse(validator.isAdult(18));
    }

    @Test
    @DisplayName("유효하지 않은 이메일은 IllegalArgumentException 예외를 던진다")
    void invalid_email_should_throw_exception() {
        // given
        String invalidEmail = "invalid-email";

        // then
        // assertThrows: 특정 예외가 발생하는지 검증합니다.
        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
            // when
            validator.validateEmailOrThrow(invalidEmail);
        });

        // 예외 메시지까지 검증할 수 있습니다.
        assertEquals("유효하지 않은 이메일 형식입니다.", exception.getMessage());
    }
}

작성한 테스트 코드가 잘 동작하는 것을 확인할 수 있다.

profile
공부 기록 공간 '◡'

0개의 댓글