Zum Inhalt springen

JUnit Comprehensive Cheatsheet

JUnit Comprehensive Cheatsheet

Installation

PlatformInstallation 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/Debiansudo apt update && sudo apt install maven (includes JUnit support)
macOSbrew install maven or brew install gradle
Windows (Chocolatey)choco install maven or choco install gradle
Standalone JARwget https://repo1.maven.org/maven2/org/junit/jupiter/junit-jupiter/5.10.1/junit-jupiter-5.10.1.jar

Core Annotations (JUnit 5)

AnnotationDescription
@TestMarks a method as a test method
@BeforeEachExecutes before each test method in the class
@AfterEachExecutes after each test method in the class
@BeforeAllExecutes once before all tests (must be static)
@AfterAllExecutes once after all tests (must be static)
@DisplayName("name")Provides custom display name for test class or method
@DisabledDisables a test class or method
@Disabled("reason")Disables with explanation message
@NestedDenotes a nested test class for organizing tests
@Tag("tagname")Tags tests for filtering (e.g., “slow”, “integration”)
@RepeatedTest(n)Repeats test execution n times
@TestFactoryMarks 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

AssertionDescription
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

AssertionDescription
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

AnnotationDescription
@ParameterizedTestMarks 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
@NullSourceProvides null as parameter
@EmptySourceProvides empty value (String, Collection, Array)
@NullAndEmptySourceCombines @NullSource and @EmptySource
@ArgumentsSource(Provider.class)Uses custom ArgumentsProvider implementation

Maven Commands

CommandDescription
mvn testRuns all tests in the project
mvn test -Dtest=ClassNameRuns specific test class
mvn test -Dtest=ClassName#methodNameRuns specific test method
mvn test -Dtest=*TestRuns all classes matching pattern
mvn test -Dtest=Class1,Class2Runs multiple specific test classes
mvn clean testCleans previous builds and runs tests
mvn test -DskipTestsSkips test execution
mvn test -Dmaven.test.skip=trueSkips test compilation and execution
mvn test -Dmaven.surefire.debugRuns tests in debug mode (port 5005)
mvn test -Dgroups=integrationRuns only tests tagged with “integration”
mvn test -DexcludedGroups=slowExcludes tests tagged with “slow”
mvn surefire-report:reportGenerates HTML test report
mvn test jacoco:reportRuns tests and generates code coverage report
mvn verifyRuns tests and integration tests
mvn test -Dsurefire.rerunFailingTestsCount=2Reruns failing tests up to 2 times

Gradle Commands

CommandDescription
gradle testRuns all tests
./gradlew testRuns tests using Gradle wrapper
gradle test --tests ClassNameRuns specific test class
gradle test --tests ClassName.methodNameRuns specific test method
gradle test --tests *TestRuns all classes matching pattern
gradle clean testCleans and runs tests
gradle test --continuousRuns tests continuously on file changes
gradle test --infoRuns tests with detailed output
gradle test --debugRuns tests with debug logging
gradle test --rerun-tasksForces test rerun even if up-to-date
gradle test --fail-fastStops execution after first test failure
gradle test --tests *Test --parallelRuns tests in parallel
gradle test jacocoTestReportRuns 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

IssueSolution
Tests not discovered/runningEnsure 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 classesAdd 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 errorMethods with @BeforeAll/@AfterAll must be static unless using @TestInstance(Lifecycle.PER_CLASS) on the test class.
Parameterized tests not runningAdd 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/GradleCheck 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 foundImport 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 displayedFor Maven, check surefire reports in target/surefire-reports/. For Gradle, check build/reports/tests/test/index.html or add testLogging configuration.
Timeout tests failing unexpectedlyUse 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 annotationsUse 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.