Configures and runs JUnit 5 (Jupiter) - modern JVM testing platform with annotations (`@Test` / `@ParameterizedTest` / `@RepeatedTest` / `@TestFactory`), lifecycle hooks (`@BeforeAll` / `@BeforeEach` / `@AfterEach` / `@AfterAll`), extension model (`@ExtendWith`), display names (`@DisplayName`), conditional execution (`@EnabledOnOs`, `@EnabledIf`), parallel execution config; integrates with Maven Surefire / Gradle test task / IntelliJ. Use when the user works with Java / Kotlin codebases needing the modern JVM standard.
80
100%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Per junit.org/junit5/docs/current/user-guide:
Three components (released 2017, replacing JUnit 4):
This skill targets JUnit Jupiter (the modern API). For Kotlin-native
tests with similar power but DSL-style, see kotest-tests.
junit-jupiter dependency and enable useJUnitPlatform() (Step 1).@Test method with an assertEquals assertion; run mvn test / ./gradlew test (Step 2).@BeforeEach / @AfterEach) for shared setup and teardown (Step 3).@ParameterizedTest with a source provider (Step 4).@ExtendWith (e.g. MockitoExtension) and annotate display names + conditions (Steps 5-6).junit-platform.properties once tests are independent (Step 7).Maven pom.xml:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>Gradle build.gradle.kts:
dependencies {
testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
}
tasks.test {
useJUnitPlatform()
}import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
void addsTwoNumbers() {
assertEquals(3, Calculator.add(1, 2));
}
}Run: mvn test or ./gradlew test.
Per j5-ug:
class UserServiceTest {
@BeforeAll
static void initAll() { /* once before all */ }
@AfterAll
static void tearDownAll() { /* once after all */ }
@BeforeEach
void init() { /* before each test */ }
@AfterEach
void tearDown() { /* after each test */ }
@Test
void test1() { ... }
@Test
void test2() { ... }
}import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
class ParametrizedTest {
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 5, 8})
void numbersInFibonacci(int n) {
assertTrue(isFibonacci(n));
}
@ParameterizedTest
@CsvSource({
"1, 2, 3",
"0, 0, 0",
"-1, 1, 0",
})
void addCases(int a, int b, int expected) {
assertEquals(expected, Calculator.add(a, b));
}
@ParameterizedTest
@MethodSource("addProvider")
void addsViaMethodSource(int a, int b, int expected) {
assertEquals(expected, Calculator.add(a, b));
}
static Stream<Arguments> addProvider() {
return Stream.of(
Arguments.of(1, 2, 3),
Arguments.of(0, 0, 0)
);
}
}Source providers: @ValueSource, @CsvSource, @CsvFileSource,
@MethodSource, @EnumSource, @ArgumentsSource.
@ExtendWith)JUnit 5's extension model (replaces JUnit 4's @Rule /
@RunWith):
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository repo;
@InjectMocks
private UserService service;
@Test
void createsUser() {
when(repo.save(any())).thenReturn(new User(1, "Alice"));
User u = service.create("Alice");
assertEquals(1, u.getId());
}
}Common extensions: MockitoExtension, SpringExtension (Spring),
SystemStubsExtension (env vars / system properties),
TempDirectory.
@DisplayName("User service")
class UserServiceTest {
@Test
@DisplayName("creates a user with email lowercased")
void createsUserWithLowercaseEmail() { ... }
@Test
@EnabledOnOs(OS.LINUX)
void linuxOnlyTest() { ... }
@Test
@EnabledIfEnvironmentVariable(named = "INTEGRATION", matches = "true")
void integrationOnly() { ... }
@Test
@Disabled("Re-enable after fixing JIRA-1234")
void temporarilyDisabled() { ... }
}Enable in junit-platform.properties:
junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = concurrent
junit.jupiter.execution.parallel.config.strategy = dynamicPer-test override:
@Execution(ExecutionMode.SAME_THREAD)
class TestNotParallelizable { ... }Wire JaCoCo coverage (Maven / Gradle) and CI reporting (GitHub Actions + Codecov; Surefire emits JUnit XML for junit-xml-analysis in the qa-test-reporting plugin) per references/coverage-and-ci.md.
Testing Calculator.add across many inputs:
org.junit.jupiter:junit-jupiter:5.11.0 (test scope) and enable useJUnitPlatform().@Test methods with one @ParameterizedTest + @CsvSource({"1, 2, 3", "0, 0, 0", "-1, 1, 0"}) taking (int a, int b, int expected).assertEquals(expected, Calculator.add(a, b)) - each row reports as its own test../gradlew test: three cases execute; the -1, 1, 0 row fails, isolating the sign bug while the others stay green.jacocoTestReport (Step 8) to confirm the add branch is now covered.| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Mix JUnit 4 + JUnit 5 in same project | Two runners, confusing | Pick Jupiter; use Vintage only for migration |
@Test from org.junit.Test (JUnit 4) | Doesn't run with Jupiter runner | Import org.junit.jupiter.api.Test (Step 2) |
| Skip parallel-execution config | Slow test suite at scale | Enable parallel.enabled (Step 7) |
Use @Disabled without ticket reference | Forgotten disabled tests | Always include reason + JIRA link (Step 6) |
Generic assertTrue(x.equals(y)) | Loses diff in failure | assertEquals(x, y) |
kotest-tests,
spock-tests,
testng-tests,
scalatest - sister toolstest-code-conventions - test code hygiene