Tooling 중급

JSpecify null 안전성을 갖춘 JUnit 6

JUnit 6은 JSpecify @NullMarked를 채택하여 전체 API에서 null 계약을 명시적으로 만듭니다.

✕ JUnit 5
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class UserServiceTest {

    // JUnit 5: no null contracts on the API
    // Can assertEquals() accept null? Check source...
    // Does fail(String) allow null message? Unknown.

    @Test
    void findUser_found() {
        // Is result nullable? API doesn't say
        User result = service.findById("u1");
        assertNotNull(result);
        assertEquals("Alice", result.name());
    }

    @Test
    void findUser_notFound() {
        // Hope this returns null, not throws...
        assertNull(service.findById("missing"));
    }
}
✓ JUnit 6
import org.junit.jupiter.api.Test;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import static org.junit.jupiter.api.Assertions.*;

@NullMarked  // all refs non-null unless @Nullable
class UserServiceTest {

    // JUnit 6 API is @NullMarked:
    // assertNull(@Nullable Object actual)
    // assertEquals(@Nullable Object, @Nullable Object)
    // fail(@Nullable String message)

    @Test
    void findUser_found() {
        // IDE warns: findById returns @Nullable User
        @Nullable User result = service.findById("u1");
        assertNotNull(result); // narrows type to non-null
        assertEquals("Alice", result.name()); // safe
    }

    @Test
    void findUser_notFound() {
        @Nullable User result = service.findById("missing");
        assertNull(result); // IDE confirms null expectation
    }
}
이 코드에 문제가 있나요? 알려주세요.
📜

명시적 계약

JUnit 6 모듈의 @NullMarked는 null 의미론을 API에 직접 문서화합니다.

🛡️

컴파일 시점 안전성

IDE와 분석기가 non-null이 예상되는 곳에 null이 전달될 때 경고하여 테스트 실행 전에 버그를 잡습니다.

🌐

에코시스템 표준

JSpecify는 Spring, Guava 등에서 채택되었습니다 — 전체 스택에서 일관된 null 의미론.

이전 방식
어노테이션 없는 API
모던 방식
@NullMarked API
JDK 버전
17
난이도
중급
JSpecify null 안전성을 갖춘 JUnit 6
사용 가능

JUnit 6.0 (2025년 10월, Java 17+ 필요) 이후 사용 가능

JUnit 5는 표준화된 null 가능성 어노테이션 없이 출시되어 어떤 매개변수가 null을 허용하는지가 불명확했습니다. JUnit 6의 JSpecify 채택은 이를 변경합니다: 전체 모듈이 @NullMarked로 표시되어 어노테이션 없는 타입이 non-null임을 의미합니다.

공유 𝕏 🦋 in