JUnit 5 将编写测试所使用的 API 与 discovers and executes them. The separation lets Maven, IDEs, and custom launchers run several test models through one interface.
The examples target JUnit 5.13.4. JUnit 6 retains the same architecture, but a project that must remain on JUnit 5 should use the versioned JUnit 5 documentation.
Platform、Jupiter 与 Vintage #
JUnit 5 consists of three components:
| Component | Responsibility |
|---|---|
| JUnit Platform | Discovers tests and launches registered test engines. Maven Surefire and IDEs integrate at this level. |
| JUnit Jupiter | Defines the JUnit 5 programming model, extension API, and test engine. |
| JUnit Vintage | Runs JUnit 3 and JUnit 4 tests on the Platform during migration. |
A Jupiter test imports @Test and assertions from org.junit.jupiter.api.
Surefire asks the Platform to discover tests, then
junit-jupiter-engine executes the discovered methods. Application code does
not depend on any JUnit component because test dependencies use Maven’s
test scope.
最小 Maven 项目 #
The following layout keeps production and test classes separate:
junit-hello/
├── pom.xml
└── src
├── main/java/com/example/Greeter.java
└── test/java/com/example/GreeterTest.javaUse org.junit.jupiter:junit-jupiter for the smallest maintainable POM. The
artifact aggregates the Jupiter API, parameterized-test support, and runtime
engine.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>junit-hello</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<junit.version>5.13.4</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.4</version>
</plugin>
</plugins>
</build>
</project>package com.example;
public final class Greeter {
public String greet(String name) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("name must not be blank");
}
return "Hello, " + name;
}
}package com.example;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
class GreeterTest {
private final Greeter greeter = new Greeter();
@Test
void greetsByName() {
assertEquals("Hello, Ada", greeter.greet("Ada"));
}
@Test
void rejectsBlankNames() {
var error = assertThrows(
IllegalArgumentException.class,
() -> greeter.greet(" "));
assertEquals("name must not be blank", error.getMessage());
}
}Run the test through Maven:
mvn testJupiter test classes and methods may have package-private visibility. Test
methods must not be private or static, although lifecycle and extension
rules permit static in specific places.
选择 JUnit 工件 #
Add another artifact only when its capability is required:
| Artifact | When to Add It |
|---|---|
junit-jupiter | Prefer the aggregate dependency for an application test suite. It already includes API, parameters, and engine. |
junit-jupiter-api | Compile tests or extensions against the API while managing runtime engines separately. |
junit-jupiter-engine | Execute Jupiter tests when using fine-grained dependencies instead of junit-jupiter. Use test runtime scope. |
junit-jupiter-params | Add @ParameterizedTest support to a fine-grained setup. The aggregate dependency already includes it. |
junit-vintage-engine | Run unchanged JUnit 3 or JUnit 4 tests through the Platform. |
junit-jupiter-migrationsupport | Temporarily adapt selected JUnit 4 rules and @Ignore while converting tests to Jupiter. |
junit-platform-suite | Declare suites with Platform suite annotations. |
junit-platform-launcher | Discover and execute tests programmatically, or implement test tooling. Application test suites do not need it directly. |
junit-platform-console-standalone | Run tests outside Maven or an IDE from one executable JAR. Keep the JAR outside the project POM. |
Import the JUnit BOM when a build declares several JUnit artifacts because the BOM keeps Platform, Jupiter, and Vintage versions compatible:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>Artifact declarations covered by the BOM can omit <version>.
An explicit junit-platform-launcher dependency was necessary with Surefire
releases before 3.0. Current Surefire 3.x releases align the launcher with the
Platform on the test runtime classpath, so adding a launcher to an ordinary
test suite creates another version to manage without adding test capability.
Jupiter 编程模型 #
Jupiter provides focused annotations for common test structures:
@BeforeEachand@AfterEachestablish per-test fixtures.@BeforeAlland@AfterAllestablish class-level fixtures.@ParameterizedTestexecutes one test body with data from@ValueSource,@CsvSource,@MethodSource, or another argument source.@Nestedgroups related cases while preserving lifecycle semantics.@Tagclassifies tests for build-time inclusion or exclusion.@ExtendWithregisters extensions for parameter resolution, callbacks, conditional execution, and other cross-cutting behavior.
Tests should assert observable results rather than execution order. Jupiter does not guarantee a self-evident order from method names, and ordered tests share state that makes failures dependent on earlier cases.
不做修改地运行 JUnit 4 测试 #
Vintage provides the lowest-risk first migration step because Jupiter and JUnit 4 use different Java packages and can coexist in one module:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>Surefire then discovers Jupiter and JUnit 4 tests through the Platform. Keep Vintage only while old tests remain because a successful Vintage run does not exercise Jupiter’s programming or extension model.
转换 JUnit 4 测试 #
Common source mappings are:
| JUnit 4 | Jupiter |
|---|---|
@Before / @After | @BeforeEach / @AfterEach |
@BeforeClass / @AfterClass | @BeforeAll / @AfterAll |
@Ignore | @Disabled |
@Category | @Tag |
@RunWith | One or more @ExtendWith extensions |
@Rule / @ClassRule | An extension or Jupiter-native fixture |
@Test(expected = E.class) | assertThrows(E.class, executable) |
@RunWith to @ExtendWith is a conceptual mapping rather than a mechanical
rename because a JUnit 4 class can have one runner while Jupiter composes
multiple extensions.
JUnit 4 places an optional assertion message first:
assertEquals("unexpected greeting", expected, actual);Jupiter places the message last:
assertEquals(expected, actual, "unexpected greeting");Existing org.junit.Assert calls can remain while annotations and lifecycle
code move to Jupiter. Converting assertions separately reduces the size of
each migration change.
Rules need individual treatment. The junit-jupiter-migrationsupport artifact
adapts ExternalResource, Verifier, and ExpectedException rule families;
@EnableRuleMigrationSupport activates those adapters.
@EnableJUnit4MigrationSupport also recognizes JUnit 4 @Ignore. Other
custom rules require a Jupiter extension or a direct fixture rewrite.
A controlled migration therefore follows three stages:
- Add Vintage and establish one Platform-based test command.
- Convert classes to Jupiter, replacing runners and rules according to their behavior rather than their names.
- Remove Vintage, JUnit 4, and migration support after the build contains no JUnit 3 or JUnit 4 tests.
评论
在 GitHub 上阅读或评论(发表评论需要登录)