Skip to content

Commit 4192165

Browse files
Copilotbrunoborges
andcommitted
Add JUnit 6 with JSpecify null safety pattern to tooling category
Co-authored-by: brunoborges <129743+brunoborges@users.noreply.github.com>
1 parent e5e7bf9 commit 4192165

File tree

3 files changed

+60
-2
lines changed

3 files changed

+60
-2
lines changed

content/language/default-interface-methods.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"state": "available",
3535
"description": "Available since JDK 8 (March 2014)."
3636
},
37-
"prev": "tooling/aot-class-preloading",
37+
"prev": "tooling/junit6-with-jspecify",
3838
"next": "language/markdown-javadoc-comments",
3939
"related": [
4040
"language/private-interface-methods",

content/tooling/aot-class-preloading.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"description": "Available as a standard feature in JDK 25 LTS (JEPs 514/515, Sept 2025)."
3636
},
3737
"prev": "tooling/built-in-http-server",
38-
"next": "language/default-interface-methods",
38+
"next": "tooling/junit6-with-jspecify",
3939
"related": [
4040
"tooling/jshell-prototyping",
4141
"tooling/single-file-execution",
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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

Comments
 (0)