Overview

The goal of this document is to provide comprehensive reference documentation for programmers writing tests, extension authors, and engine authors as well as build tool and IDE vendors.

What is JUnit?

JUnit is composed of several different modules from three different sub-projects.

JUnit 6.1.3 = JUnit Platform + JUnit Jupiter + JUnit Vintage

The JUnit Platform serves as a foundation for launching testing frameworks on the JVM. It also defines the TestEngine API for developing a testing framework that runs on the platform. Furthermore, the platform provides a Console Launcher to launch the platform from the command line and the JUnit Platform Suite Engine for running a custom test suite using one or more test engines on the platform. First-class support for the JUnit Platform also exists in popular IDEs (see IntelliJ IDEA, Eclipse, NetBeans, and Visual Studio Code) and build tools (see Gradle, Maven, Ant, Bazel, and sbt).

JUnit Jupiter is the combination of the programming model and extension model for writing JUnit tests and extensions. The Jupiter sub-project provides a TestEngine for running Jupiter based tests on the platform.

JUnit Vintage provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the platform. It requires JUnit 4.12 or later to be present on the class path or module path. Note, however, that the JUnit Vintage engine is deprecated and should only be used temporarily while migrating tests to JUnit Jupiter or another testing framework with native JUnit Platform support.

Supported Java Versions

JUnit requires Java 17 (or higher) at runtime. However, you can still test code that has been compiled with previous versions of the JDK.

Getting Help

Ask JUnit-related questions on Stack Overflow or use the Q&A category on GitHub Discussions.

Getting Started

Downloading JUnit Artifacts

To find out what artifacts are available for download and inclusion in your project, refer to Dependency Metadata. To set up dependency management for your build, refer to Build Support and the Example Projects.

JUnit Features

To find out what features are available in JUnit 6.1.3 and how to use them, read the corresponding sections of this User Guide, organized by topic.

Example Projects

To see complete, working examples of projects that you can copy and experiment with, the junit-examples repository is a good place to start. The junit-examples repository hosts a collection of example projects based on JUnit Jupiter, JUnit Vintage, and other testing frameworks. You’ll find appropriate build scripts (e.g., build.gradle, pom.xml, etc.) in the example projects. The links below highlight some of the combinations you can choose from.

Writing Tests

The following example provides a glimpse at the minimum requirements for writing a test in JUnit Jupiter. Subsequent sections of this chapter will provide further details on all available features.

A first test case
Java
import static org.junit.jupiter.api.Assertions.assertEquals;

import example.util.Calculator;

import org.junit.jupiter.api.Test;

class MyFirstJUnitJupiterTests {

	private final Calculator calculator = new Calculator();

	@Test
	void addition() {
		assertEquals(2, calculator.add(1, 1));
	}
}
Kotlin
import example.util.Calculator
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class MyFirstJUnitJupiterTests {
    private val calculator = Calculator()

    @Test
    fun addition() {
        assertEquals(2, calculator.add(1, 1))
    }
}

Annotations

JUnit Jupiter supports the following annotations for configuring tests and extending the framework.

Unless otherwise stated, all core annotations are located in the org.junit.jupiter.api package in the junit-jupiter-api module.

@Test

Denotes that a method is a test method. Unlike JUnit 4’s @Test annotation, this annotation does not declare any attributes, since test extensions in JUnit Jupiter operate based on their own dedicated annotations. Such methods are inherited unless they are overridden.

@ParameterizedTest

Denotes that a method is a parameterized test. Such methods are inherited unless they are overridden.

@RepeatedTest

Denotes that a method is a test template for a repeated test. Such methods are inherited unless they are overridden.

@TestFactory

Denotes that a method is a test factory for dynamic tests. Such methods are inherited unless they are overridden.

@TestTemplate

Denotes that a method is a template for a test case designed to be invoked multiple times depending on the number of invocation contexts returned by the registered providers. Such methods are inherited unless they are overridden.

@TestClassOrder

Used to configure the test class execution order for @Nested test classes in the annotated test class. Such annotations are inherited.

@TestMethodOrder

Used to configure the test method execution order for the annotated test class; similar to JUnit 4’s @FixMethodOrder. Such annotations are inherited.

@TestInstance

Used to configure the test instance lifecycle for the annotated test class. Such annotations are inherited.

@DisplayName

Declares a custom display name for the test class or test method. Such annotations are not inherited.

@DisplayNameGeneration

Declares a custom display name generator for the test class. Such annotations are inherited.

@BeforeEach

Denotes that the annotated method should be executed before each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class; analogous to JUnit 4’s @Before. Such methods are inherited unless they are overridden.

@AfterEach

Denotes that the annotated method should be executed after each @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory method in the current class; analogous to JUnit 4’s @After. Such methods are inherited unless they are overridden.

@BeforeAll

Denotes that the annotated method should be executed before all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current top-level or @Nested test class; analogous to JUnit 4’s @BeforeClass. Such methods are inherited unless they are overridden and must be static unless the "per-class" test instance lifecycle is used.

@AfterAll

Denotes that the annotated method should be executed after all @Test, @RepeatedTest, @ParameterizedTest, and @TestFactory methods in the current top-level or @Nested test class; analogous to JUnit 4’s @AfterClass. Such methods are inherited unless they are overridden and must be static unless the "per-class" test instance lifecycle is used.

@ParameterizedClass

Denotes that the annotated class is a parameterized class. Such annotations are inherited.

@BeforeParameterizedClassInvocation

Denotes that the annotated method should be executed once before each invocation of a parameterized class. Such methods are inherited unless they are overridden.

@AfterParameterizedClassInvocation

Denotes that the annotated method should be executed once after each invocation of a parameterized class. Such methods are inherited unless they are overridden.

@ClassTemplate

Denotes that the annotated class is a template for a test class designed to be executed multiple times depending on the number of invocation contexts returned by the registered providers. Such annotations are inherited.

@Nested

Denotes that the annotated class is a non-static nested test class. Such annotations are not inherited.

@Tag

Used to declare tags for filtering tests, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4. Such annotations are inherited at the class level but not at the method level.

@Disabled

Used to disable a test class or test method; analogous to JUnit 4’s @Ignore. Such annotations are not inherited.

@AutoClose

Denotes that the annotated field represents a resource that will be automatically closed after test execution. Such fields are inherited.

@Timeout

Used to fail a test, test factory, test template, or lifecycle method if its execution exceeds a given duration. Such annotations are inherited.

@TempDir

Used to supply a temporary directory via field injection or parameter injection in a test class constructor, lifecycle method, or test method; located in the org.junit.jupiter.api.io package. Such fields are inherited.

@ExtendWith

Used to register extensions declaratively. Such annotations are inherited.

@RegisterExtension

Used to register extensions programmatically via fields. Such fields are inherited.

Some annotations may currently be experimental. Consult the table in Experimental APIs for details.

Meta-Annotations and Composed Annotations

JUnit Jupiter annotations can be used as meta-annotations. That means that you can define your own composed annotation that will automatically inherit the semantics of its meta-annotations.

For example, instead of copying and pasting @Tag("fast") throughout your code base (see Tagging and Filtering), you can create a custom composed annotation named @Fast as follows. @Fast can then be used as a drop-in replacement for @Tag("fast").

Java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.jupiter.api.Tag;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
public @interface Fast {
}
Kotlin
import org.junit.jupiter.api.Tag

@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@Tag("fast")
annotation class Fast

The following @Test method demonstrates usage of the @Fast annotation.

Java
@Fast
@Test
void myFastTest() {
    // ...
}
Kotlin
@Fast
@Test
fun myFastTest() {
    // ...
}

You can even take that one step further by introducing a custom @FastTest annotation that can be used as a drop-in replacement for @Tag("fast") and @Test.

Java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Tag("fast")
@Test
public @interface FastTest {
}
Kotlin
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@Tag("fast")
@Test
annotation class FastTest

JUnit automatically recognizes the following as a @Test method that is tagged with "fast".

Java
@FastTest
void myFastTest() {
    // ...
}
Kotlin
@FastTest
fun myFastTest() {
    // ...
}

Definitions

Platform Concepts

Container

a node in the test tree that contains other containers or tests as its children (e.g. a test class).

Test

a node in the test tree that verifies expected behavior when executed (e.g. a @Test method).

Jupiter Concepts

Lifecycle Method

any method that is directly annotated or meta-annotated with @BeforeAll, @AfterAll, @BeforeEach, or @AfterEach.

Test Class

any top-level class, static member class, or @Nested class that contains at least one test method, i.e. a container. Test classes must not be abstract and must have a single constructor. Java record classes are supported as well.

Test Method

any instance method that is directly annotated or meta-annotated with @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, or @TestTemplate. With the exception of @Test, these create a container in the test tree that groups tests or, potentially (for @TestFactory), other containers.

Test Classes and Methods

Test methods and lifecycle methods may be declared locally within the current test class, inherited from superclasses, or inherited from interfaces (see Test Interfaces and Default Methods). In addition, test methods and lifecycle methods must not be abstract and must not return a value (except @TestFactory methods which are required to return a value).

Class and method visibility

Test classes, test methods, and lifecycle methods are not required to be public, but they must not be private.

It is generally recommended to omit the public modifier for test classes, test methods, and lifecycle methods unless there is a technical reason for doing so – for example, when a test class is extended by a test class in another package. Another technical reason for making classes and methods public is to simplify testing on the module path when using the Java Module System.

Field and method inheritance

Fields in test classes are inherited. For example, a @TempDir field from a superclass will always be applied in a subclass.

Test methods and lifecycle methods are inherited unless they are overridden according to the visibility rules of the Java language. For example, a @Test method from a superclass will always be applied in a subclass unless the subclass explicitly overrides the method. Similarly, if a package-private @Test method is declared in a superclass that resides in a different package than the subclass, that @Test method will always be applied in the subclass since the subclass cannot override a package-private method from a superclass in a different package.

The following test class demonstrates the use of @Test methods and all supported lifecycle methods. For further information on runtime semantics, see Test Execution Order and Wrapping Behavior of Callbacks.

A standard test class
Java
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

class StandardTests {

	@BeforeAll
	static void initAll() {
	}

	@BeforeEach
	void init() {
	}

	@Test
	void succeedingTest() {
	}

	@Test
	void failingTest() {
		fail("a failing test");
	}

	@Test
	@Disabled("for demonstration purposes")
	void skippedTest() {
		// not executed
	}

	@Test
	void abortedTest() {
		assumeTrue("abc".contains("Z"));
		fail("test should have been aborted");
	}

	@AfterEach
	void tearDown() {
	}

	@AfterAll
	static void tearDownAll() {
	}

}
Kotlin
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assumptions.assumeTrue
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.fail

class StandardTests {
    @BeforeEach
    fun init() {
    }

    @Test
    fun succeedingTest() {
    }

    @Test
    fun failingTest() {
        fail("a failing test")
    }

    @Test
    @Disabled("for demonstration purposes")
    fun skippedTest() {
        // not executed
    }

    @Test
    fun abortedTest() {
        assumeTrue("abc".contains("Z"))
        fail("test should have been aborted")
    }

    @AfterEach
    fun tearDown() {
    }

    companion object {
        @JvmStatic
        @BeforeAll
        fun initAll() {
        }

        @JvmStatic
        @AfterAll
        fun tearDownAll() {
        }
    }
}

It is also possible to use Java record classes as test classes as illustrated by the following example.

A test class written as a Java record
import static org.junit.jupiter.api.Assertions.assertEquals;

import example.util.Calculator;

import org.junit.jupiter.api.Test;

record MyFirstJUnitJupiterRecordTests() {

	@Test
	void addition() {
		assertEquals(2, new Calculator().add(1, 1));
	}

}

Kotlin Coroutines

Test methods and lifecycle methods may be written in Kotlin and may optionally use the suspend keyword for testing code using coroutines.

A test class written in Kotlin
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test

class KotlinCoroutinesDemo {
    @BeforeEach
    fun regularSetUp() {
    }

    @BeforeEach
    suspend fun coroutineSetUp() {
    }

    @Test
    fun regularTest() {
    }

    @Test
    suspend fun coroutineTest() {
    }
}
Using suspending functions as test or lifecycle methods requires kotlin-stdlib, kotlin-reflect, and kotlinx-coroutines-core to be present on the classpath or module path.

When using suspend test or lifecycle methods, JUnit internally uses runBlocking to execute them. This is sufficient for simple coroutine-based tests.

However, runBlocking does not support skipping calls to delay and does not provide control over virtual time or dispatchers.

As an alternative, tests can be written as regular @Test methods and wrapped in runTest from