Tooling Средний

JUnit 6 принимает @NullMarked из JSpecify, делая null-контракты явными во всём Assertion API.

✕ 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
    }
}
Заметили проблему в этом коде? Сообщите нам.
📜

Явные контракты

@NullMarked в модуле JUnit 6 документирует семантику null прямо в API — читать исходный код не нужно.

🛡️

Безопасность на этапе компиляции

IDE и анализаторы предупреждают при передаче null туда, где ожидается ненулевое значение, выявляя ошибки до запуска тестов.

🌐

Стандарт экосистемы

JSpecify принят Spring, Guava и другими библиотеками — единая семантика null во всём стеке.

Старый подход
API без аннотаций
Современный подход
@NullMarked API
Начиная с JDK
17
Сложность
Средний
JUnit 6 с типобезопасностью JSpecify по null
Доступно

Доступно начиная с JUnit 6.0 (октябрь 2025, требуется Java 17+)

JUnit 5 поставлялся без стандартизированных аннотаций допустимости null, поэтому разработчики вынуждены были догадываться, могут ли параметры или возвращаемые значения методов утверждения быть null. JUnit 6 принимает JSpecify для всего своего модуля: аннотация @NullMarked делает все неаннотированные типы ненулевыми по умолчанию, а @Nullable отмечает исключения. Класс Assertions явно аннотирует параметры — например, assertNull(@Nullable Object actual) и fail(@Nullable String message), — благодаря чему IDE и статические анализаторы, такие как NullAway и Error Prone, могут обнаруживать ошибки работы с null во время компиляции, а не во время выполнения.