junit5-tests
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.
Install with skills.sh (any agent)
npx skills add testland/qa --skill junit5-testsjunit5-tests
Overview
Per junit.org/junit5/docs/current/user-guide (opens in new window):
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.
How to use
Step 1 - Install (Maven / Gradle)
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()
}Step 2 - First test
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.
Step 3 - Lifecycle annotations
Per j5-ug (opens in new window):
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() { ... }
}Step 4 - Parameterized tests
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.
Step 5 - Extensions (@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.
Step 6 - Display names + conditional execution
@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() { ... }
}Step 7 - Parallel execution
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 { ... }Step 8 - Coverage and CI
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.
Worked example
Testing Calculator.add across many inputs:
Anti-patterns
| 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) |
Limitations
References
JUnit 5 - coverage and CI
View source (opens in new window)JUnit 5 - coverage and CI
Coverage with JaCoCo
Maven:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<goals><goal>prepare-agent</goal></goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals><goal>report</goal></goals>
</execution>
<execution>
<id>jacoco-check</id>
<goals><goal>check</goal></goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>CI integration
- run: ./gradlew test jacocoTestReport
- uses: codecov/codecov-action@v4
with: { files: ./build/reports/jacoco/test/jacocoTestReport.xml }Surefire (Maven) emits JUnit XML for junit-xml-analysis (in the qa-test-reporting plugin).
Related skills
assertj
Reference for AssertJ - the canonical JVM fluent-assertion library pairable with JUnit 5 / TestNG / Spock; covers the assertThat() entry point, collection matchers (contains, containsExactly, allSatisfy, extracting), exception assertions (assertThatThrownBy, catchThrowable), SoftAssertions for multi-failure collection, recursive comparison (usingRecursiveComparison) for deep equality, and domain-specific custom assertions via AbstractAssert. Use when writing JVM tests that need richer failure messages than built-in assertEquals, or when verifying complex object graphs, exception types, or collections.
kotest-tests
Configures and runs Kotest - Kotlin-native test framework with multiple specification styles (StringSpec, FunSpec, BehaviorSpec, DescribeSpec, ShouldSpec, FreeSpec, FeatureSpec, ExpectSpec, AnnotationSpec); rich matcher library; built-in property-based testing (alternative to jqwik); coroutines support; data-driven testing; isolation modes per-spec or per-test; integrates with Gradle JVM test task. Use when working with Kotlin and wanting Kotlin-idiomatic DSL over JUnit 5's annotation-driven approach. Matchers (shouldBe, shouldContain) are bundled with the runner and are not a drop-in replacement for a standalone JVM assertion library; for assertion-only use paired with JUnit 5 / TestNG / Spock see assertj.
scalatest
Configures and runs ScalaTest - Scala-native test framework with multiple specification styles (FlatSpec, FunSuite, WordSpec, FreeSpec, AsyncFlatSpec for async); Matchers DSL (`should equal`, `should contain`, `shouldBe a [Class]`); integrates with ScalaCheck for property-based testing; sbt + Maven + Gradle support; tagged-test selective execution. Use when working with Scala codebases.
spock-tests
Configures and runs Spock - Groovy-based JVM testing framework with given/when/then BDD blocks, where: data tables for parametrized tests, built-in mocking via Mock()/Stub()/Spy(), interaction-based testing (verify method calls in declarative DSL), implicit assertions in then: blocks. Use when working with Java/Kotlin codebases that benefit from Groovy DSL expressiveness, or maintaining existing Spock projects.
testng-tests
Configures and runs TestNG - JVM testing framework with `@Test` priorities + groups + `dependsOnMethods`; `@DataProvider` for parametrized tests with method-level data sources; `testng.xml` suite definitions for grouping + parallelism config; listeners (`ITestListener`, `ISuiteListener`) for hooks; `ITestContext` for cross-test state; integrates with Maven Surefire / Gradle. Use when working with legacy TestNG codebases or needing TestNG-specific features (test method dependencies, suite-level XML config).