Skip to main content

Junit 5

Mastering JUnit 5 @TestFactory: Dynamic Testing Beyond Parameterized Tests

In the world of Java unit testing, we are mostly accustomed to the standard @Test annotation. These are static tests—their structure and logic are set in stone the moment you compile your code. But anyone who has tried to test rule engines, data migrations, or complex validation pipelines knows the pain: either you duplicate dozens of nearly identical tests or force-fit everything into a parameterized test that eventually becomes unreadable. What if you need to generate hundreds of test cases based on a JSON file, a database result set, or complex runtime logic? Enter Dynamic Testing with JUnit 5. In this comprehensive guide, we’ll explore the @TestFactory annotation, how it differs from traditional testing models, and how to leverage it to make your test suites more flexible, maintainable, and powerful. What is a Dynamic Test? Standard JUnit tests are "fixed." When you write a method annotated with @Test, JUnit knows exactly how many tests exist before the execution starts. A DynamicTest, however, is a test generated during runtime by a factory method. Think of it as the difference between a static HTML page and a React application. One is hardcoded; the other builds its UI based on the data it receives. Dynamic tests allow you to programmatically create a series of tests based on a data source, a list of objects, or even a continuous stream of events.

JUnit 5 with Gradle & Maven: From Setup to Professional Testing

In the modern Java ecosystem, developer productivity is often measured by the speed and reliability of the feedback loop. As a Java developer, two tools you’ll inevitably encounter are Gradle (or Maven) for building your projects and JUnit for testing them. JUnit 5, the latest generation of this popular testing framework, isn't just an incremental update; it’s a complete modular overhaul designed to support modern Java features (like Lambdas) and provide a more robust extension model. Integrating it into your build lifecycle is a fundamental skill. This guide will walk you through the architecture, configuration, and execution of JUnit 5 tests, ensuring your setup is optimized for high-performance development. ⚡ Quick Start: TL;DR Setup If you’re in a hurry or just need a quick reference, here are the essential steps for your build files: Gradle (Groovy) Add Dependencies: junit-jupiter-api (compile) and junit-jupiter-engine (runtime). Enable Platform: Add useJUnitPlatform() inside the test { ... } block. Maven Add Dependency: Include the junit-jupiter aggregator artifact. Plugin Check: Ensure maven-surefire-plugin is version 2.22.0 or higher. A Deep Dive into the JUnit 5 Architecture To configure your build tool correctly, you must first understand that JUnit 5 is a modular framework composed of three main sub-projects. Unlike JUnit 4, which was a monolithic library, JUnit 5 separates concerns to allow for better integration with IDEs and build tools. 1. JUnit Platform This is the core foundation. It defines the TestEngine API, which tools like IntelliJ IDEA, Gradle, and Maven use to discover and launch tests.

The Complete Guide to JUnit 5 @ParameterizedTest: Write Smarter, Faster, and Cleaner Java Tests

Every official way to feed a @ParameterizedTest, built and run against a real JUnit 6.1.3 module: @ValueSource through @ArgumentsSource, how display names actually compile to a MessageFormat pattern, two real anti-patterns caught failing on purpose, and three genuine bugs found by rebuilding this guide's original examples from scratch instead of copying them forward.

Organising and Displaying JUnit Tests: @DisplayName, @Nested, @Tag (JUnit 5 → JUnit 6)

A beginner-first, fully verified guide to @DisplayName, DisplayNameGenerator, @Nested, and @Tag in JUnit — every code example and console transcript comes from a real Maven module built and run against JUnit 6.1.3, the current GA release verified against Maven Central. Covers what changed for these three annotations between JUnit 5 and JUnit 6, including a real behaviour change (deterministic @Nested ordering) that most migration guides mention only in passing.

JUnit Conditional Test Execution (JUnit 5 → 6)

Run tests only where they belong. Every conditional-execution example from my 2025 article, re-run on JUnit 6.1.3 and JDK 25 with the real skip reasons, what changed from JUnit 5, and where the old text was wrong.

JUnit @RepeatedTest in JUnit 6: RepetitionInfo, failureThreshold, and Real Flaky-Test Detection

JUnit's @RepeatedTest runs the same method N times, each with its own pass/fail result. Rebuilt here against real JUnit 6.1.3 on JDK 25: all four RepetitionInfo accessors (not just two), RepetitionInfo injected into @BeforeEach/@AfterEach, what failureThreshold actually does when it fires (skips, not stops), a real flaky-test detection demo, and a placeholder the original version of this post claimed existed but never did.

Understanding JUnit 5 @AfterEach Annotation with Practical Examples

This post has been merged into a single, updated guide covering both @BeforeEach and @AfterEach together, with a per-method lifecycle diagram and a JUnit 6 compatibility note: JUnit @BeforeEach and @AfterEach: The Complete Test Lifecycle Guide. Everything that was here…

JUnit @BeforeAll and @AfterAll: One-Time Setup and Teardown

How JUnit @BeforeAll and @AfterAll work on JUnit 6 and 5: why they are static, how @TestInstance(PER_CLASS) lifts that, what happens when setup throws, and how @TempDir and the extension Store clean up shared resources for you. Every claim is backed by a run in a companion repository.