Skill

Write JUnit 5 Tests with AI Coding Assistants

A skill for generating JUnit 5 tests - assertions, parameterized tests, Mockito mocking, and nested test grouping.

Works with junitmockitogithub

35
Spark score
out of 100
Updated last month
Version 1.0.0

Add to Favorites

Why it matters

Enable AI coding assistants to generate production-grade JUnit 5 test automation code for Java services and applications, with built-in support for Mockito mocking and CI/CD integration through GitHub Actions.

Outcomes

What it gets done

01

Generate JUnit 5 test classes with proper annotations and assertions for Java services

02

Create unit tests with Mockito mocks and stubs for dependency isolation

03

Configure GitHub Actions CI pipelines for automated test execution

04

Write parameterized and nested test suites following JUnit 5 best practices

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/ag-junit-5-skill | bash

Overview

JUnit 5 Testing Skill

This skill generates JUnit 5 tests in Java: assertions, parameterized tests (ValueSource/CsvSource/MethodSource), Mockito mocking, and nested test grouping, with Maven dependencies and anti-pattern guidance. Use it when writing JUnit 5 unit tests, parameterized tests, Mockito-mocked tests, or Spring integration tests in Java.

What it does

A skill for generating production-grade JUnit 5 unit and integration tests in Java, covering assertions, parameterized tests, lifecycle hooks, Mockito mocking, and nested tests. It routes on keywords - "unit test"/"assert" for a standard test, "parameterized"/"multiple inputs" for @ParameterizedTest, "mock"/"Mockito" for a test with mocking, "integration test"/"Spring" pointing to a reference/spring-integration.md doc, defaulting to a standard unit test otherwise. Core patterns show @BeforeEach setup, @Test with @DisplayName, assertThrows for exceptions, and assertAll for grouped assertions that report all failures together rather than stopping at the first. The assertions reference covers assertEquals/assertNotEquals/assertTrue/assertFalse/assertNull/assertNotNull/assertThrows/assertTimeout/assertAll/assertIterableEquals. Parameterized tests cover @ValueSource for a literal list of inputs, @CsvSource for multi-argument rows, @MethodSource pointing to a static Stream<Arguments> provider method, and @NullAndEmptySource combined with @ValueSource for blank-string edge cases. Mockito mocking uses @ExtendWith(MockitoExtension.class) with @Mock fields and @InjectMocks on the class under test, stubbing with when()/thenReturn() and verifying calls with verify(), including a not-found case returning Optional.empty() and asserting the resulting exception. Nested tests use @Nested classes with their own @DisplayName to group related test cases (e.g. "when creating a user" and "when deleting a user" as sibling nested classes). A documented anti-pattern table contrasts non-descriptive test names with descriptive ones, testing private methods versus testing through the public API, omitting @DisplayName versus always adding one, and assertEquals(true, x) versus the more readable assertTrue(x). Maven dependencies are junit-jupiter 5.11.0 and mockito-junit-jupiter 5.14.0, both test-scoped. A quick-reference table covers running all tests (mvn test / gradlew test), a single class or method, tagged tests via @Tag plus -Dgroups, @Disabled with a reason, OS-conditional execution via @EnabledOnOs, @Timeout, @RepeatedTest, and @TestMethodOrder for explicit ordering. A deeper reference/playbook.md covers project setup (Maven deps, parallel config, surefire), test lifecycle (BeforeAll/Each, ordering, tags), deeper parameterized testing (CsvSource, MethodSource, EnumSource, ValueSource), Mockito (captors, verify order), nested and dynamic tests (@TestFactory), and AssertJ fluent assertions.

When to use - and when NOT to

Use it when the user mentions JUnit, JUnit 5, @Test, assertEquals, Assertions, or "Java unit test" - needing standard unit tests, parameterized tests, Mockito-based mocking, or Spring integration tests.

Inputs and outputs

Given Java code to test, it produces a JUnit 5 test class using the appropriate pattern - standard assertions, @ParameterizedTest with the right source annotation, Mockito-mocked dependencies, or @Nested grouping - following the documented naming and anti-pattern guidance.

Integrations

<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.11.0</version></dependency>

Uses JUnit 5 (Jupiter) as the test framework, Mockito for mocking dependencies, and integrates with Maven or Gradle for running tests via CLI commands.

Who it's for

Java developers writing JUnit 5 tests - unit tests, parameterized tests across multiple inputs, mocked service tests, or grouped nested test suites - who want correct annotation choice and awareness of common anti-patterns like testing private methods or vague test names.

Source README

JUnit 5 Testing Skill

When to Use

Use this skill when you need generates production-grade JUnit 5 unit and integration tests in Java. Covers assertions, parameterized tests, lifecycle hooks, mocking with Mockito, and nested tests. Use when user mentions "JUnit", "JUnit 5", "@Test", "assertEquals", "Assertions", "Java unit test". Triggers on:...

You are a senior Java developer specializing in JUnit 5 testing.

Step 1 - Test Type

├─ "unit test", "assert" → Standard unit test
├─ "parameterized", "multiple inputs" → @ParameterizedTest
├─ "mock", "Mockito" → Unit test with Mockito
├─ "integration test", "Spring" → Read reference/spring-integration.md
└─ Default → Standard unit test

Core Patterns

Basic Test

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

class CalculatorTest {
    private Calculator calculator;

    @BeforeEach
    void setUp() {
        calculator = new Calculator();
    }

    @Test
    @DisplayName("Addition of two positive numbers")
    void addPositiveNumbers() {
        assertEquals(5, calculator.add(2, 3));
    }

    @Test
    void divideByZero_throwsException() {
        assertThrows(ArithmeticException.class, () -> calculator.divide(10, 0));
    }

    @Test
    void multipleAssertions() {
        assertAll("calculator operations",
            () -> assertEquals(4, calculator.add(2, 2)),
            () -> assertEquals(0, calculator.subtract(2, 2)),
            () -> assertEquals(6, calculator.multiply(2, 3))
        );
    }
}

Assertions Reference

assertEquals(expected, actual);
assertNotEquals(unexpected, actual);
assertTrue(condition);
assertFalse(condition);
assertNull(object);
assertNotNull(object);
assertThrows(IllegalArgumentException.class, () -> service.process(null));
assertTimeout(Duration.ofSeconds(2), () -> service.longRunningOp());
assertAll("group",
    () -> assertNotNull(user.getName()),
    () -> assertTrue(user.getAge() > 0)
);
assertIterableEquals(List.of(1, 2, 3), actualList);

Parameterized Tests

@ParameterizedTest
@ValueSource(strings = {"hello", "world", "junit"})
void stringIsNotEmpty(String value) {
    assertFalse(value.isEmpty());
}

@ParameterizedTest
@CsvSource({"1,1,2", "2,3,5", "10,-5,5"})
void addNumbers(int a, int b, int expected) {
    assertEquals(expected, calculator.add(a, b));
}

@ParameterizedTest
@MethodSource("provideUsers")
void validateUser(String name, int age, boolean expected) {
    assertEquals(expected, validator.isValid(name, age));
}

static Stream<Arguments> provideUsers() {
    return Stream.of(
        Arguments.of("Alice", 25, true),
        Arguments.of("", 25, false),
        Arguments.of("Bob", -1, false)
    );
}

@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {"  ", "\t"})
void blankStringsAreInvalid(String input) {
    assertFalse(validator.isValid(input));
}

Mocking with Mockito

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @Mock private UserRepository userRepo;
    @Mock private EmailService emailService;
    @InjectMocks private UserService userService;

    @Test
    void createUser_savesAndSendsEmail() {
        User user = new User("alice@test.com", "Alice");
        when(userRepo.save(any(User.class))).thenReturn(user);

        User result = userService.createUser("alice@test.com", "Alice");

        assertNotNull(result);
        verify(userRepo).save(any(User.class));
        verify(emailService).sendWelcomeEmail("alice@test.com");
    }

    @Test
    void getUser_notFound_throwsException() {
        when(userRepo.findById(99L)).thenReturn(Optional.empty());
        assertThrows(UserNotFoundException.class, () -> userService.getUser(99L));
    }
}

Nested Tests

@DisplayName("UserService")
class UserServiceTest {
    @Nested
    @DisplayName("when creating a user")
    class CreateUser {
        @Test void withValidData_succeeds() { }
        @Test void withDuplicateEmail_throwsException() { }
    }

    @Nested
    @DisplayName("when deleting a user")
    class DeleteUser {
        @Test void existingUser_removesFromDb() { }
        @Test void nonExistentUser_throwsException() { }
    }
}

Anti-Patterns

Bad Good Why
@Test public void test1() @Test void shouldCalculateSum() Descriptive names
Testing private methods Test via public API Implementation detail
No @DisplayName Always add display names Better reporting
assertEquals(true, x) assertTrue(x) More readable

Maven Dependencies

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.11.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>5.14.0</version>
    <scope>test</scope>
</dependency>

Quick Reference

Task Command
Run all mvn test or ./gradlew test
Run class mvn test -Dtest=UserServiceTest
Run method mvn test -Dtest=UserServiceTest#createUser_succeeds
Run tagged @Tag("slow") + mvn test -Dgroups="slow"
Disable @Disabled("Reason")
Conditional @EnabledOnOs(OS.LINUX)
Timeout @Timeout(value = 5, unit = TimeUnit.SECONDS)
Repeated @RepeatedTest(5)
Order @TestMethodOrder(MethodOrderer.OrderAnnotation.class)

Deep Patterns

For production-grade patterns, see reference/playbook.md:

Section What's Inside
§1 Project Setup Maven deps, parallel config, surefire
§2 Test Lifecycle BeforeAll/Each, ordering, tags
§3 Parameterized CsvSource, MethodSource, EnumSource, ValueSource
§4 Mockito @Mock/@InjectMocks, captor, verify order
§5 Nested & Dynamic @Nested grouping, @TestFactory
§6 AssertJ Fluent assertions, extracting, collection checks
§7 Conditional @EnabledOnOs, assumptions, @EnabledIf
§8 Custom Extensions Timing, retry, BeforeTestExecution
§9 CI/CD GitHub Actions with test reporter
§10 Debugging Table 8 common problems with fixes
§11 Best Practices 12-item production checklist

Limitations

  • Use this skill only when the task clearly matches its upstream source and local project context.
  • Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
  • Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.