Skip to content

JUnit Comprehensive Cheatsheet

Installation

Platform Installation Method
Maven (JUnit 5) Add to pom.xml: <dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.10.1</version><scope>test</scope></dependency>
Maven (JUnit 4) Add to pom.xml: <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency>
Gradle (JUnit 5) Add to build.gradle: testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' and test { useJUnitPlatform() }
Gradle (JUnit 4) Add to build.gradle: testImplementation 'junit:junit:4.13.2'
Ubuntu/Debian sudo apt update && sudo apt install maven (includes JUnit support)
macOS brew install maven or brew install gradle
Windows (Chocolatey) choco install maven or choco install gradle
Standalone JAR wget https://repo1.maven.org/maven2/org/junit/jupiter/junit-jupiter/5.10.1/junit-jupiter-5.10.1.jar

Core Annotations (JUnit 5)

Annotation Description
@Test Marks a method as a test method
@BeforeEach Executes before each test method in the class
@AfterEach Executes after each test method in the class
@BeforeAll Executes once before all tests (must be static)
@AfterAll Executes once after all tests (must be static)
@DisplayName("name") Provides custom display name for test class or method
@Disabled Disables a test class or method
@Disabled("reason") Disables with explanation message
@Nested Denotes a nested test class for organizing tests
@Tag("tagname") Tags tests for filtering (e.g., "slow", "integration")
@RepeatedTest(n) Repeats test execution n times
@TestFactory Marks a method as a dynamic test factory
@TestInstance(Lifecycle.PER_CLASS) Changes test instance lifecycle
@Timeout(value = 5) Fails test if execution exceeds 5 seconds
@Order(n) Specifies test execution order when using @TestMethodOrder

Basic Assertions

Assertion Description
assertEquals(expected, actual) Asserts that two values are equal
assertEquals(expected, actual, "msg") Asserts equality with custom failure message
assertEquals(expected, actual, delta) Asserts floating-point equality within delta
assertNotEquals(unexpected, actual) Asserts that two values are not equal
assertTrue(condition) Asserts that condition is true
assertFalse(condition) Asserts that condition is false
assertNull(object) Asserts that object is null
assertNotNull(object) Asserts that object is not null
assertSame(expected, actual) Asserts that two references point to same object
assertNotSame(obj1, obj2) Asserts that two references point to different objects
assertArrayEquals(expected, actual) Asserts that two arrays are equal
assertIterableEquals(expected, actual) Asserts that two iterables are equal
assertLinesMatch(expected, actual) Asserts that two lists of strings match (supports regex)
fail("message") Explicitly fails a test with message
assertAll("group", () -> {...}) Groups multiple assertions (all execute even if some fail)

Advanced Assertions

Assertion Description
assertThrows(Exception.class, () -> {...}) Asserts that executable throws specified exception
assertDoesNotThrow(() -> {...}) Asserts that executable does not throw any exception
assertTimeout(Duration.ofSeconds(2), () -> {...}) Asserts that execution completes within timeout
assertTimeoutPreemptively(Duration, () -> {...}) Asserts timeout and aborts execution if exceeded
assertInstanceOf(Class.class, object) Asserts that object is instance of specified class
assertAll(() -> {...}, () -> {...}) Executes all assertions and reports all failures

Parameterized Test Annotations

Annotation Description
@ParameterizedTest Marks method as parameterized test
@ValueSource(ints = {1, 2, 3}) Provides array of literal values as parameters
@ValueSource(strings = {"a", "b"}) Provides array of string values
@CsvSource({"1,2,3", "4,5,9"}) Provides CSV data as parameters
@CsvFileSource(resources = "/data.csv") Loads parameters from CSV file
@MethodSource("methodName") Uses method return value as parameter source
@EnumSource(MyEnum.class) Uses enum values as parameters
@NullSource Provides null as parameter
@EmptySource Provides empty value (String, Collection, Array)
@NullAndEmptySource Combines @NullSource and @EmptySource
@ArgumentsSource(Provider.class) Uses custom ArgumentsProvider implementation

Maven Commands

Command Description
mvn test Runs all tests in the project
mvn test -Dtest=ClassName Runs specific test class
mvn test -Dtest=ClassName#methodName Runs specific test method
mvn test -Dtest=*Test Runs all classes matching pattern
mvn test -Dtest=Class1,Class2 Runs multiple specific test classes
mvn clean test Cleans previous builds and runs tests
mvn test -DskipTests Skips test execution
mvn test -Dmaven.test.skip=true Skips test compilation and execution
mvn test -Dmaven.surefire.debug Runs tests in debug mode (port 5005)
mvn test -Dgroups=integration Runs only tests tagged with "integration"
mvn test -DexcludedGroups=slow Excludes tests tagged with "slow"
mvn surefire-report:report Generates HTML test report
mvn test jacoco:report Runs tests and generates code coverage report
mvn verify Runs tests and integration tests
mvn test -Dsurefire.rerunFailingTestsCount=2 Reruns failing tests up to 2 times

Gradle Commands

Command Description
gradle test Runs all tests
./gradlew test Runs tests using Gradle wrapper
gradle test --tests ClassName Runs specific test class
gradle test --tests ClassName.methodName Runs specific test method
gradle test --tests *Test Runs all classes matching pattern
gradle clean test Cleans and runs tests
gradle test --continuous Runs tests continuously on file changes
gradle test --info Runs tests with detailed output
gradle test --debug Runs tests with debug logging
gradle test --rerun-tasks Forces test rerun even if up-to-date
gradle test --fail-fast Stops execution after first test failure
gradle test --tests *Test --parallel Runs tests in parallel
gradle test jacocoTestReport Runs tests and generates coverage report

Configuration

Maven Surefire Plugin Configuration

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.2.2</version>
    <configuration>
        <!-- Include/exclude test patterns -->
        <includes>
            <include>**/*Test.java</include>
            <include>**/*Tests.java</include>
        </includes>
        <excludes>
            <exclude>**/*IntegrationTest.java</exclude>
        </excludes>

        <!-- Parallel execution -->
        <parallel>methods</parallel>
        <threadCount>4</threadCount>

        <!-- Tag filtering -->
        <groups>unit, integration</groups>
        <excludedGroups>slow</excludedGroups>

        <!-- System properties -->
        <systemPropertyVariables>
            <env>test</env>
        </systemPropertyVariables>
    </configuration>
</plugin>

Gradle Test Configuration

test {
    useJUnitPlatform {
        // Include/exclude tags
        includeTags 'unit', 'integration'
        excludeTags 'slow'

        // Include/exclude engines
        includeEngines 'junit-jupiter'
        excludeEngines 'junit-vintage'
    }

    // Parallel execution
    maxParallelForks = 4

    // Test filtering
    filter {
        includeTestsMatching '*Test'
        excludeTestsMatching '*IntegrationTest'
    }

    // System properties
    systemProperty 'env', 'test'

    // Test logging
    testLogging {
        events "passed", "skipped", "failed"
        exceptionFormat "full"
        showStandardStreams = true
    }

    // Fail fast
    failFast = true
}

JUnit Platform Properties (junit-platform.properties)

# Parallel execution
junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = concurrent
junit.jupiter.execution.parallel.config.strategy = fixed
junit.jupiter.execution.parallel.config.fixed.parallelism = 4

# Test instance lifecycle
junit.jupiter.testinstance.lifecycle.default = per_class

# Display name generation
junit.jupiter.displayname.generator.default = org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores

# Automatic extension detection
junit.jupiter.extensions.autodetection.enabled = true

# Deactivate conditions
junit.jupiter.conditions.deactivate = org.junit.*DisabledCondition

Common Use Cases

Use Case: Basic Unit Test with Setup and Teardown

import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

class UserServiceTest {
    private UserService userService;
    private Database mockDb;

    @BeforeEach
    void setUp() {
        mockDb = new MockDatabase();
        userService = new UserService(mockDb);
    }

    @Test
    @DisplayName("Should create user with valid data")
    void testCreateUser() {
        User user = new User("john@example.com", "password123");
        User created = userService.createUser(user);

        assertNotNull(created.getId());
        assertEquals("john@example.com", created.getEmail());
    }

    @AfterEach
    void tearDown() {
        mockDb.close();
    }
}

Use Case: Parameterized Testing for Multiple Inputs

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;

class CalculatorTest {

    @ParameterizedTest
    @CsvSource({
        "1, 1, 2",
        "2, 3, 5",
        "10, -5, 5",
        "0, 0, 0"
    })
    void testAddition(int a, int b, int expected) {
        Calculator calc = new Calculator();
        assertEquals(expected, calc.add(a, b));
    }

    @ParameterizedTest
    @ValueSource(strings = {"", "  ", "\t", "\n"})
    void testBlankStrings(String input) {
        assertTrue(StringUtils.isBlank(input));
    }

    @ParameterizedTest
    @MethodSource("provideEmailTestCases")
    void testEmailValidation(String email, boolean expected) {
        assertEquals(expected, EmailValidator.isValid(email));
    }

    static Stream<Arguments> provideEmailTestCases() {
        return Stream.of(
            Arguments.of("test@example.com", true),
            Arguments.of("invalid.email", false),
            Arguments.of("@example.com", false)
        );
    }
}

Use Case: Exception Testing and Timeout Verification

import org.junit.jupiter.api.Test;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.*;

class PaymentServiceTest {

    @Test
    void shouldThrowExceptionForInvalidAmount() {
        PaymentService service = new PaymentService();

        Exception exception = assertThrows(
            IllegalArgumentException.class,
            () -> service.processPayment(-100)
        );

        assertEquals("Amount must be positive", exception.getMessage());
    }

    @Test
    void shouldCompleteWithinTimeout() {
        PaymentService service = new PaymentService();

        assertTimeout(Duration.ofSeconds(2), () -> {
            service.processPayment(100);
        });
    }

    @Test
    void shouldNotThrowExceptionForValidPayment() {
        PaymentService service = new PaymentService();

        assertDoesNotThrow(() -> {
            service.processPayment(100);
        });
    }
}

Use Case: Nested Tests for Organized Test Structure

import org.junit.jupiter.api.*;

@DisplayName("Shopping Cart Tests")
class ShoppingCartTest {

    @Nested
    @DisplayName("When cart is empty")
    class EmptyCart {
        private ShoppingCart cart;

        @BeforeEach
        void setUp() {
            cart = new ShoppingCart();
        }

        @Test
        @DisplayName("Should have zero items")
        void testItemCount() {
            assertEquals(0, cart.getItemCount());
        }

        @Test
        @DisplayName("Should have zero total")
        void testTotal() {
            assertEquals(0.0, cart.getTotal());
        }
    }

    @Nested
    @DisplayName("When cart has items")
    class CartWithItems {
        private ShoppingCart cart;

        @BeforeEach
        void setUp() {
            cart = new ShoppingCart();
            cart.addItem(new Item("Book", 29.99));
            cart.addItem(new Item("Pen", 2.50));
        }

        @Test
        @DisplayName("Should calculate correct total")
        void testTotal() {
            assertEquals(32.49, cart.getTotal(), 0.01);
        }

        @Test
        @DisplayName("Should allow item removal")
        void testRemoveItem() {
            cart.removeItem(0);
            assertEquals(1, cart.getItemCount());
        }
    }
}

Use Case: Integration Testing with Test Lifecycle Management

import org.junit.jupiter.api.*;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DatabaseIntegrationTest {
    private static DatabaseConnection connection;
    private UserRepository userRepository;

    @BeforeAll
    void setUpDatabase() {
        // Expensive one-time setup
        connection = DatabaseConnection.connect("jdbc:h2:mem:test");
        connection.runMigrations();
    }

    @BeforeEach
    void setUp() {
        userRepository = new UserRepository(connection);
        connection.beginTransaction();
    }

    @Test
    @Tag("integration")
    void testUserPersistence() {
        User user = new User("test@example.com");
        userRepository.save(user);

        User retrieved = userRepository.findById(user.getId());
        assertEquals(user.getEmail(), retrieved.getEmail());
    }

    @AfterEach
    void tearDown() {
        connection.rollbackTransaction();
    }

    @AfterAll
    void tearDownDatabase() {
        connection.close();
    }
}

Best Practices

  • Use descriptive test names: Leverage @DisplayName to make test purposes clear. Test names should describe what is being tested and the expected outcome (e.g., "Should throw exception when amount is negative").

  • Follow AAA pattern: Structure tests with Arrange (setup), Act (execute), and Assert (verify) sections for clarity and maintainability.

  • Test one thing per test: Each test method should verify a single behavior or scenario. This makes failures easier to diagnose and tests easier to maintain.

  • Use @BeforeEach and @AfterEach wisely: Initialize common test fixtures in @BeforeEach but avoid excessive setup that makes tests hard to understand. Clean up resources in @AfterEach.

  • Prefer @ParameterizedTest for multiple similar cases: Instead of writing multiple similar test methods, use parameterized tests to test the same logic with different inputs.

  • Use assertAll for related assertions: Group related assertions with assertAll() to see all failures at once rather than stopping at the first failure.

  • Tag tests appropriately: Use @Tag to categorize tests (unit, integration, slow) so you can selectively run test suites in different environments or CI/CD stages.

  • Avoid test interdependencies: Tests should be independent and able to run in any order. Never rely on execution order or state from other tests.

  • Use meaningful assertion messages: Provide custom failure messages to assertions to make debugging easier: assertEquals(expected, actual, "User email should match").

  • Keep tests fast: Unit tests should execute quickly. Move slow tests (database, network) to separate integration test suites using tags or separate source sets.

Troubleshooting

Issue Solution
Tests not discovered/running Ensure test methods are public or package-private, annotated with @Test, and class names match pattern *Test or *Tests. For Maven, verify surefire plugin version ≥ 2.22.0 for JUnit 5 support.
NoClassDefFoundError for JUnit classes Add JUnit dependency with <scope>test</scope> in Maven or testImplementation in Gradle. Verify correct JUnit version (5.x for Jupiter, 4.x for legacy).
@BeforeAll/@AfterAll must be static error Methods with @BeforeAll/@AfterAll must be static unless using @TestInstance(Lifecycle.PER_CLASS) on the test class.
Parameterized tests not running Add junit-jupiter-params dependency: org.junit.jupiter:junit-jupiter-params:5.10.1. Ensure method has @ParameterizedTest instead of @Test.
Tests pass in IDE but fail in Maven/Gradle Check for different JUnit versions between IDE and build tool. Verify maven-surefire-plugin version ≥ 2.22.0 or Gradle has useJUnitPlatform() in test configuration.
Assertion methods not found Import static assertion methods: import static org.junit.jupiter.api.Assertions.*; for JUnit 5 or import static org.junit.Assert.*; for JUnit 4.
Tests run but results not displayed For Maven, check surefire reports in target/surefire-reports/. For Gradle, check build/reports/tests/test/index.html or add testLogging configuration.
Timeout tests failing unexpectedly Use assertTimeoutPreemptively() instead of assertTimeout() if test code doesn't respect interruption. Check for blocking I/O or synchronization issues.
Cannot mix JUnit 4 and JUnit 5 annotations Use JUnit Vintage engine to run JUnit 4 tests alongside JUnit 5, or migrate all tests to JUnit 5. Add junit-vintage-engine dependency for backward compatibility.
Gradle tests always UP-TO-DATE Use gradle cleanTest test or gradle test --rerun-tasks to force test execution. Check if test source files are in correct directory (src/test/java).