본문으로 건너뛰기
안동민 개발노트 아이콘

안동민 개발노트

본문 시작
8장 : 테스팅 전략

JUnit 6로 도메인 규칙 고정

테스트 범위를 크게 잡는다고 더 정확해지는 것은 아닙니다. StudySession의 분량 제한과 상태 전이는 Spring Bean이나 데이터베이스 없이도 결정되는 순수 Java 규칙입니다. 이런 규칙은 컨텍스트를 시작하지 않는 JUnit 테스트로 가장 빠르고 명확하게 고정합니다.


가장 빠른 테스트는 순수 규칙을 겨냥한다

Arrange–Act–Assert는 annotation이나 필수 문법이 아니라 테스트의 원인과 결과를 읽기 쉽게 나누는 방법입니다. 입력을 준비하고, 한 가지 행동을 실행하고, 관찰 가능한 결과를 단언합니다. 세 단계가 뒤섞이면 어떤 조건이 실패를 만들었는지 찾기 어려워집니다.

정상값 하나만 확인하지 말고 경계값과 거부값을 함께 둡니다. 이 예제에서는 1분과 1440분이 허용 경계이고, 0과 1441은 실패 경계입니다.


정상·경계·실패 사례를 JUnit으로 표현한다

먼저 상한값 1440이 보존되는지 확인하고, 이어서 0분 입력이 도메인 생성 단계에서 거부되는지 확인합니다. HTTP 상태나 DB 예외가 아니라 도메인이 공개한 결과와 예외만 단언합니다.

src/test/java/com/andongmin/studylog/session/StudySessionTest.java
package com.andongmin.studylog.session;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.time.LocalDate;
import java.util.UUID;
import org.junit.jupiter.api.Test;

class StudySessionTest {
    @Test
    void acceptsBoundaryMinutes() {
        var session = new StudySession(UUID.randomUUID(), "Spring", "JUnit",
                LocalDate.of(2026, 7, 13), 1440, SessionStatus.PLANNED, 0);

        assertEquals(1440, session.minutes());
    }

    @Test
    void rejectsZeroMinutes() {
        IllegalArgumentException failure = assertThrows(IllegalArgumentException.class,
                () -> new StudySession(UUID.randomUUID(), "Spring", "JUnit",
                        LocalDate.of(2026, 7, 13), 0, SessionStatus.PLANNED, 0));

        assertEquals("minutes must be between 1 and 1440", failure.getMessage());
    }
}

실패 테스트는 예외 타입뿐 아니라 메시지도 확인합니다. 메시지가 외부 API 계약이라면 전용 예외와 안정된 오류 코드를 쓰는 편이 좋지만, 여기서는 규칙이 어느 경계에서 거부됐는지를 교육용으로 명확히 드러냅니다.


실패 메시지가 규칙을 설명하는지 확인한다

아래 명령은 Spring context를 시작하지 않고 이 클래스만 실행합니다. 실행 시간이 갑자기 길어졌다면 테스트 코드가 모르게 Spring extension이나 외부 자원에 의존하기 시작하지 않았는지 살펴봅니다.

./mvnw -q -Dtest=StudySessionTest test
Assert 확인 결과
Spring context를 시작하지 않고 두 테스트가 빠르게 통과해야 합니다.

테스트 이름은 규칙과 기대 결과를 함께 말한다

acceptsBoundaryMinutes, rejectsZeroMinutes처럼 이름만 읽어도 어떤 입력이 어떤 결과를 만드는지 알 수 있게 작성합니다. 구현 메서드 이름을 그대로 옮긴 testConstructor1 같은 이름은 리팩터링에 약하고 실패 보고서에서도 도움을 주지 못합니다.


도메인 규칙을 가장 작은 범위에서 고정했으니 다음에는 Controller가 이 규칙을 HTTP 요청과 응답으로 올바르게 번역하는지만 분리해서 검증합니다.