|
| 1 | +{ |
| 2 | + "id": 111, |
| 3 | + "slug": "junit6-with-jspecify", |
| 4 | + "title": "JUnit 6 with JSpecify null safety", |
| 5 | + "category": "tooling", |
| 6 | + "difficulty": "intermediate", |
| 7 | + "jdkVersion": "17", |
| 8 | + "oldLabel": "JUnit 5", |
| 9 | + "modernLabel": "JUnit 6", |
| 10 | + "oldApproach": "Unannotated API", |
| 11 | + "modernApproach": "@NullMarked API", |
| 12 | + "oldCode": "import org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass UserServiceTest {\n\n // JUnit 5: no null contracts on the API\n // Can assertEquals() accept null? Check source...\n // Does fail(String) allow null message? Unknown.\n\n @Test\n void findUser_found() {\n // Is result nullable? API doesn't say\n User result = service.findById(\"u1\");\n assertNotNull(result);\n assertEquals(\"Alice\", result.name());\n }\n\n @Test\n void findUser_notFound() {\n // Hope this returns null, not throws...\n assertNull(service.findById(\"missing\"));\n }\n}", |
| 13 | + "modernCode": "import org.junit.jupiter.api.Test;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport static org.junit.jupiter.api.Assertions.*;\n\n@NullMarked // all refs non-null unless @Nullable\nclass UserServiceTest {\n\n // JUnit 6 API is @NullMarked:\n // assertNull(@Nullable Object actual)\n // assertEquals(@Nullable Object, @Nullable Object)\n // fail(@Nullable String message)\n\n @Test\n void findUser_found() {\n // IDE warns: findById returns @Nullable User\n @Nullable User result = service.findById(\"u1\");\n assertNotNull(result); // narrows type to non-null\n assertEquals(\"Alice\", result.name()); // safe\n }\n\n @Test\n void findUser_notFound() {\n @Nullable User result = service.findById(\"missing\");\n assertNull(result); // IDE confirms null expectation\n }\n}", |
| 14 | + "summary": "JUnit 6 adopts JSpecify @NullMarked, making null contracts explicit across its assertion API.", |
| 15 | + "explanation": "JUnit 5 shipped without standardized nullability annotations, leaving developers to guess whether assertion parameters or return values could be null. JUnit 6 adopts JSpecify across its entire module: the @NullMarked annotation makes all unannotated types non-null by default, and @Nullable marks the exceptions. The Assertions class explicitly annotates parameters such as assertNull(@Nullable Object actual) and fail(@Nullable String message), so IDEs and static analyzers like NullAway and Error Prone can catch null misuse at compile time instead of at runtime.", |
| 16 | + "whyModernWins": [ |
| 17 | + { |
| 18 | + "icon": "📜", |
| 19 | + "title": "Explicit contracts", |
| 20 | + "desc": "@NullMarked on the JUnit 6 module documents null semantics directly in the API — no source-reading required." |
| 21 | + }, |
| 22 | + { |
| 23 | + "icon": "🛡️", |
| 24 | + "title": "Compile-time safety", |
| 25 | + "desc": "IDEs and analyzers warn when null is passed where non-null is expected, catching bugs before tests run." |
| 26 | + }, |
| 27 | + { |
| 28 | + "icon": "🌐", |
| 29 | + "title": "Ecosystem standard", |
| 30 | + "desc": "JSpecify is adopted by Spring, Guava, and others — consistent null semantics across your whole stack." |
| 31 | + } |
| 32 | + ], |
| 33 | + "support": { |
| 34 | + "state": "available", |
| 35 | + "description": "Available since JUnit 6.0 (October 2025, requires Java 17+)" |
| 36 | + }, |
| 37 | + "prev": "tooling/aot-class-preloading", |
| 38 | + "next": "language/default-interface-methods", |
| 39 | + "related": [ |
| 40 | + "enterprise/spring-null-safety-jspecify", |
| 41 | + "errors/helpful-npe", |
| 42 | + "errors/require-nonnull-else" |
| 43 | + ], |
| 44 | + "docs": [ |
| 45 | + { |
| 46 | + "title": "JUnit 6 Assertions API", |
| 47 | + "href": "https://docs.junit.org/current/api/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html" |
| 48 | + }, |
| 49 | + { |
| 50 | + "title": "JSpecify Nullness User Guide", |
| 51 | + "href": "https://jspecify.dev/docs/user-guide/" |
| 52 | + }, |
| 53 | + { |
| 54 | + "title": "Upgrading to JUnit 6.0", |
| 55 | + "href": "https://github.com/junit-team/junit-framework/wiki/Upgrading-to-JUnit-6.0/Core-Principles" |
| 56 | + } |
| 57 | + ] |
| 58 | +} |
0 commit comments