[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M786JQAV8Q28S0YRVXQHQ4D5":3,"skill-same:01M786JQAV8Q28S0YRVXQHQ4D5":49},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":29,"commitSha":30,"skillDirectory":31,"forksCount":14,"repositoryHtmlUrl":32,"repositoryHomepageUrl":6,"ownerHtmlUrl":33,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":34,"install":35,"resources":48},"01M786JQAV8Q28S0YRVXQHQ4D5","java-testing",null,"Use ONLY when user explicitly requests Java test code, JUnit\u002FMockito\u002FAssertJ configuration, test failure diagnosis, or coverage improvement for Java projects.\nTriggers: \"write test java\", \"JUnit\", \"Mockito\", \"AssertJ\", \"coverage java\", \"flaky test java\", \"Spring Boot test\", \"Testcontainers\", \"test slice\", \"jacoco\".\nDo NOT activate for: general Java coding without test context, Maven\u002FGradle build configuration outside test scope, Spring bean wiring, or non-test dependency issues.","chenziyang110\u002Flaunchdeck","chenziyang110","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43693913?v=4","01M7863EBWK1D0P6A29YGEMAS4","Robin11","01M7863ECM1VB6RXDMHTW90D2T",0,"MIT","https:\u002F\u002Fgithub.com\u002Fchenziyang110\u002Flaunchdeck\u002Fblob\u002Fmain\u002F.codex\u002Fskills\u002Fjava-testing\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: \"java-testing\"\ndescription: \"Use ONLY when user explicitly requests Java test code, JUnit\u002FMockito\u002FAssertJ configuration,\ntest failure diagnosis, or coverage improvement for Java projects.\n\nTriggers: \\\"write test java\\\", \\\"JUnit\\\", \\\"Mockito\\\", \\\"AssertJ\\\", \\\"coverage java\\\",\n\\\"flaky test java\\\", \\\"Spring Boot test\\\", \\\"Testcontainers\\\", \\\"test slice\\\", \\\"jacoco\\\".\n\nDo NOT activate for: general Java coding without test context, Maven\u002FGradle build\nconfiguration outside test scope, Spring bean wiring, or non-test dependency issues.\n\"\ncompatibility: \"Requires spec-kit project structure with .specify\u002F directory\"\nmetadata:\n  author: \"github-spec-kit\"\n  source: \"templates\u002Fpassive-skills\u002Fjava-testing\u002FSKILL.md\"\n---\n\n\n# Java Testing (Agent Skill)\n\nAgent-focused testing workflow for modern Java (17+) using JUnit 5 with Mockito, the industry-standard combination for Java testing.\n\n## When to Use\n\n- Writing new Java tests or fixing existing tests\n- Designing unit\u002Fintegration test coverage for Java components\n- Adding test coverage, CI gating, or regression protection\n- Configuring JUnit 5 \u002F Mockito workflows for consistent execution\n- Investigating test failures or flaky behavior\n- Testing Spring Boot, REST APIs, or library code\n\n### When NOT to Use\n\n- Implementing new product features without test changes\n- Large-scale refactors unrelated to test coverage or failures\n- Performance tuning without test regressions to validate\n- Non-Java projects or non-test tasks\n\n### Stop Conditions — When to ASK instead of generating\n\nBefore generating any test code, STOP and ask the user if:\n- The code under test has **zero testability** (no DI, global state, static method chains, no interfaces)\n- **No test framework** is detected in the project (no JUnit\u002FMockito in pom.xml or build.gradle)\n- The request is **ambiguous**: \"test my app\" or \"add tests\" without specifying files or components\n- A **required test dependency** (JUnit, Mockito, AssertJ) is not installed\n- The test would require **secrets, real credentials, or production database access**\n\n## Core Concepts\n\n- **TDD loop**: red → green → refactor (tests first, minimal fix, then cleanups).\n- **Isolation**: prefer constructor injection and mocks over global state.\n- **Test layout**: `src\u002Ftest\u002Fjava\u002F` with same package structure as source, `src\u002Ftest\u002Fresources\u002F`.\n- **Mocks vs stubs**: Mockito mocks for behavior verification, stubs for state.\n- **Parametrized tests**: `@ParameterizedTest` with `@ValueSource`, `@CsvSource`, `@MethodSource`.\n- **CI signal**: run subset first, then full suite with `--fail-if-no-tests`.\n\n## Agent Execution Protocol\n\nWhen this skill is activated, follow this exact execution sequence:\n\n### Phase 1: Context Analysis\nBefore writing any test code:\n1. Identify the Java version (17+) and build tool (Maven\u002FGradle).\n2. Check for existing test files (`src\u002Ftest\u002Fjava\u002F`) to infer package structure and style (JUnit 5, AssertJ, Mockito).\n3. Analyze the code under test: identify public API surface, side effects, Spring annotations, and concurrency.\n4. Determine request type: (a) new tests, (b) fixing broken tests, (c) coverage improvement, (d) flaky test diagnosis.\n\n### Phase 2: Strategy Selection\nBased on Phase 1, select the appropriate strategy:\n- **Pure functions \u002F value objects**: unit tests only, no mocks needed. Use `@ParameterizedTest` with `@CsvSource`.\n- **External dependencies (DB, HTTP, filesystem)**: use Mockito; prefer Testcontainers for real DB integration tests.\n- **Spring Boot components**: use Test Slices (`@WebMvcTest`, `@DataJpaTest`, `@RestClientTest`) instead of heavy `@SpringBootTest`.\n- **Async code**: use `CompletableFuture` testing or Awaitility for bounded waits.\n\n### Decision Tree (Complex Scenarios)\n\n```text\nQ1: External I\u002FO (DB, HTTP, filesystem, network)?\n  → Yes → Q2\n  → No  → Pure unit test, no mocks needed\n\nQ2: Mockable at code boundary (@Inject\u002F@Autowired, constructor DI, interface)?\n  → Yes → Mock at boundary (Mockito @Mock + @InjectMocks)\n  → No  → Q3\n\nQ3: Needs real behavior verification (SQL, JPA, transactions)?\n  → Yes → @DataJpaTest \u002F Testcontainers (real DB in Docker)\n  → No  → Use fake\u002Fstub implementation\n\nQ4: Async\u002Fconcurrent (CompletableFuture, @Async, reactive)?\n  → Yes → Awaitility for bounded waits, StepVerifier for reactive\n  → No  → Synchronous test sufficient\n\nQ5: Spring MVC\u002FREST controller?\n  → Yes → @WebMvcTest with MockMvc (10-100x faster than @SpringBootTest)\n  → No  → Standard unit test with JUnit 5\n```\n\n### Phase 3: Test Case Design\nApply systematic test design before coding:\n1. **Happy path**: normal input → expected output.\n2. **Boundary values**: empty collections, `null`, `Optional.empty()`, max integer\u002Fstring length.\n3. **Error cases**: exceptions (`assertThrows`), validation errors, 4xx\u002F5xx responses.\n4. **State & concurrency**: mutable shared state, thread-safety, transaction rollback.\n\n### Phase 4: Code Generation\nOutput tests in this exact structure:\n```\n#### Test Strategy Summary\n- [ ] Scope: Unit \u002F Integration \u002F E2E\n- [ ] Framework: JUnit 5 + Mockito + AssertJ\n- [ ] Mocking: Yes\u002FNo — Mockito \u002F Testcontainers\n- [ ] Edge cases covered: \u003Clist>\n\n#### Generated Test Code\n\u003Cfile_path>\n\u003Ccode>\n\n#### Running Commands\n```bash\n# Maven\nmvn test -Dtest=ClassName#methodName\n# Gradle\n.\u002Fgradlew test --tests \"ClassName.methodName\"\n```\n\n#### Coverage Impact (if applicable)\n- Estimated coverage change: \u003CX%>\n- Command to verify: `mvn test jacoco:report` \u002F `.\u002Fgradlew test jacocoTestReport`\n```\n\n### Phase 5: Verification Checklist\nBefore completing, verify:\n- [ ] Tests compile without errors (`mvn test-compile` \u002F `.\u002Fgradlew compileTestJava`).\n- [ ] Each test has exactly one logical assertion focus (use `assertAll` for grouped assertions).\n- [ ] Mocks are reset between tests (`@MockitoSettings` or `@BeforeEach`).\n- [ ] No `Thread.sleep` used for synchronization (use Awaitility).\n- [ ] Flaky test guardrails applied (`@TempDir`, deterministic data, `@Tag` separation).\n\n## TDD Workflow\n\nFollow the RED → GREEN → REFACTOR loop:\n\n1. **RED**: write a failing test that captures the new behavior\n2. **GREEN**: implement the smallest change to pass\n3. **REFACTOR**: clean up while tests stay green\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FAddTest.java\nclass AddTest {\n    @Test \u002F\u002F RED\n    void addsTwoNumbers() {\n        assertEquals(5, Add.add(2, 3));\n    }\n}\n\n\u002F\u002F src\u002Fmain\u002Fjava\u002Fcom\u002Fexample\u002FAdd.java\npublic class Add { \u002F\u002F GREEN\n    public static int add(int a, int b) {\n        return a + b;\n    }\n}\n\n\u002F\u002F REFACTOR: simplify\u002Frename once tests pass\n```\n\n## Code Examples\n\n### Basic Unit Test (JUnit 5)\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FCalculatorTest.java\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass CalculatorTest {\n    @Test\n    void addsTwoNumbers() {\n        assertEquals(5, Calculator.add(2, 3));\n    }\n}\n```\n\n### Lifecycle (JUnit 5)\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FUserStoreTest.java\nimport org.junit.jupiter.api.*;\nimport java.util.*;\n\nclass UserStoreTest {\n    private UserStore store;\n\n    @BeforeEach\n    void setUp() {\n        store = new UserStore();\n        store.seed(List.of(new User(\"alice\"), new User(\"bob\")));\n    }\n\n    @Test\n    void findsExistingUser() {\n        Optional\u003CUser> user = store.find(\"alice\");\n        assertTrue(user.isPresent());\n        assertEquals(\"alice\", user.get().name());\n    }\n}\n```\n\n### Mocking (Mockito)\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FNotifierTest.java\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mockito;\nimport static org.mockito.Mockito.*;\n\nclass NotifierTest {\n    @Test\n    void sendsNotifications() {\n        Notifier notifier = mock(Notifier.class);\n        Service service = new Service(notifier);\n\n        service.publish(\"hello\");\n\n        verify(notifier, times(1)).send(\"hello\");\n    }\n}\n```\n\n### Mockito with Annotations\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FOrderServiceTest.java\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.*;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport static org.mockito.Mockito.*;\nimport static org.junit.jupiter.api.Assertions.*;\n\n@ExtendWith(MockitoExtension.class)\nclass OrderServiceTest {\n    @Mock\n    private PaymentGateway paymentGateway;\n\n    @InjectMocks\n    private OrderService orderService;\n\n    @Test\n    void processesPayment() {\n        when(paymentGateway.charge(anyDouble())).thenReturn(true);\n\n        boolean result = orderService.processOrder(100.0);\n\n        assertTrue(result);\n        verify(paymentGateway).charge(100.0);\n    }\n}\n```\n\n### Parametrized Test\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FMathTest.java\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.*;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass MathTest {\n    @ParameterizedTest\n    @CsvSource({\n        \"1, 2, 3\",\n        \"-1, 1, 0\",\n        \"0, 0, 0\",\n        \"100, 200, 300\"\n    })\n    void addsNumbers(int a, int b, int expected) {\n        assertEquals(expected, Math.add(a, b));\n    }\n}\n```\n\n### Exception Testing\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FValidationTest.java\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass ValidationTest {\n    @Test\n    void throwsOnNegativeAge() {\n        assertThrows(IllegalArgumentException.class,\n            () -> Validator.validateAge(-1));\n    }\n\n    @Test\n    void throwsWithMessage() {\n        Exception ex = assertThrows(IllegalArgumentException.class,\n            () -> Validator.validateAge(-1));\n        assertTrue(ex.getMessage().contains(\"non-negative\"));\n    }\n}\n```\n\n### AssertJ Fluent Assertions\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FUserTest.java\nimport org.junit.jupiter.api.Test;\nimport static org.assertj.core.api.Assertions.*;\n\nclass UserTest {\n    @Test\n    void userHasCorrectProperties() {\n        User user = new User(\"alice\", \"alice@example.com\");\n\n        assertThat(user.name()).isEqualTo(\"alice\");\n        assertThat(user.email()).endsWith(\"@example.com\");\n        assertThat(user.roles()).contains(\"user\");\n    }\n}\n```\n\n## Build Configuration\n\n### Maven\n\n```xml\n\u003C!-- pom.xml -->\n\u003Cdependencies>\n    \u003Cdependency>\n        \u003CgroupId>org.junit.jupiter\u003C\u002FgroupId>\n        \u003CartifactId>junit-jupiter\u003C\u002FartifactId>\n        \u003Cversion>5.12.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n    \u003Cdependency>\n        \u003CgroupId>org.mockito\u003C\u002FgroupId>\n        \u003CartifactId>mockito-core\u003C\u002FartifactId>\n        \u003Cversion>5.16.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n    \u003Cdependency>\n        \u003CgroupId>org.assertj\u003C\u002FgroupId>\n        \u003CartifactId>assertj-core\u003C\u002FartifactId>\n        \u003Cversion>3.27.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n    \u003C!-- Integration testing -->\n    \u003Cdependency>\n        \u003CgroupId>org.testcontainers\u003C\u002FgroupId>\n        \u003CartifactId>testcontainers\u003C\u002FartifactId>\n        \u003Cversion>1.20.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n    \u003Cdependency>\n        \u003CgroupId>org.awaitility\u003C\u002FgroupId>\n        \u003CartifactId>awaitility\u003C\u002FartifactId>\n        \u003Cversion>4.2.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n\u003C\u002Fdependencies>\n\n\u003Cbuild>\n    \u003Cplugins>\n        \u003Cplugin>\n            \u003CgroupId>org.apache.maven.plugins\u003C\u002FgroupId>\n            \u003CartifactId>maven-surefire-plugin\u003C\u002FartifactId>\n            \u003Cversion>3.2.5\u003C\u002Fversion>\n        \u003C\u002Fplugin>\n    \u003C\u002Fplugins>\n\u003C\u002Fbuild>\n```\n\n### Gradle\n\n```kotlin\n\u002F\u002F build.gradle.kts\ndependencies {\n    testImplementation(\"org.junit.jupiter:junit-jupiter:5.12.0\")\n    testImplementation(\"org.mockito:mockito-core:5.16.0\")\n    testImplementation(\"org.assertj:assertj-core:3.27.0\")\n    testImplementation(\"org.testcontainers:testcontainers:1.20.0\")\n    testImplementation(\"org.awaitility:awaitility:4.2.0\")\n}\n\ntasks.test {\n    useJUnitPlatform()\n    testLogging {\n        events(\"passed\", \"skipped\", \"failed\")\n    }\n}\n```\n\n## Running Tests\n\n```bash\nmvn test                           # run all tests\nmvn test -Dtest=CalculatorTest     # run single test class\nmvn test -Dtest=CalculatorTest#addsTwoNumbers  # run single method\nmvn test -Dtest=\"*Test\"            # wildcard pattern\nmvn test -pl module-name           # run tests in a module\n\n.\u002Fgradlew test                     # run all tests\n.\u002Fgradlew test --tests \"*CalculatorTest*\"  # filter by pattern\n.\u002Fgradlew test --tests \"*CalculatorTest.addsTwoNumbers\"  # single test\n.\u002Fgradlew test --info              # verbose output\n```\n\n## Debugging Failures\n\n1. Re-run the single failing test with `@Tag` filtering or `--tests` pattern.\n2. Pass `-X` to Maven or `--stacktrace` to Gradle for full stack traces.\n3. Add breakpoints and run with debugger (`mvn test -Dmaven.surefire.debug`).\n4. Use `System.out` (captured by surefire by default).\n5. Expand to full suite once the root cause is fixed.\n\n## Coverage\n\n### JaCoCo (Maven)\n\n```xml\n\u003Cplugin>\n    \u003CgroupId>org.jacoco\u003C\u002FgroupId>\n    \u003CartifactId>jacoco-maven-plugin\u003C\u002FartifactId>\n    \u003Cversion>0.8.12\u003C\u002Fversion>\n    \u003Cexecutions>\n        \u003Cexecution>\n            \u003Cgoals>\u003Cgoal>prepare-agent\u003C\u002Fgoal>\u003C\u002Fgoals>\n        \u003C\u002Fexecution>\n        \u003Cexecution>\n            \u003Cid>report\u003C\u002Fid>\n            \u003Cphase>test\u003C\u002Fphase>\n            \u003Cgoals>\u003Cgoal>report\u003C\u002Fgoal>\u003C\u002Fgoals>\n        \u003C\u002Fexecution>\n    \u003C\u002Fexecutions>\n\u003C\u002Fplugin>\n```\n\n### JaCoCo (Gradle)\n\n```kotlin\nplugins {\n    id(\"jacoco\")\n}\n\ntasks.jacocoTestReport {\n    dependsOn(tasks.test)\n    reports {\n        xml.required.set(true)\n        html.required.set(true)\n    }\n}\n```\n\n```bash\nmvn test jacoco:report\n.\u002Fgradlew test jacocoTestReport\n```\n\n## CI\u002FCD Integration (GitHub Actions)\n\n```yaml\n# .github\u002Fworkflows\u002Ftest.yml\nname: Test\non: [push, pull_request]\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: actions\u002Fsetup-java@v4\n        with: { java-version: '21', distribution: 'temurin' }\n      - run: mvn test jacoco:report\n      - uses: actions\u002Fupload-artifact@v4\n        if: always()\n        with: { name: test-results, path: target\u002Fsurefire-reports\u002F*.xml }\n```\n\n## Flaky Tests Guardrails\n\n- Never use `Thread.sleep()` for synchronization; use `awaitility`.\n- Make temp directories unique per test with `@TempDir`.\n- Avoid real time, network, or filesystem dependencies in unit tests.\n- Use `java.time.Clock` injection for time-dependent code.\n- Use `awaitility` for async assertions with timeout.\n\n## Test Case Design Patterns\n\nApply these systematic patterns before writing test code to ensure robust coverage beyond the happy path.\n\n### 1. Boundary Value Analysis\nTest at the edges of valid input domains: empty, zero, maximum, null, and just-outside valid ranges.\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FValidatorTest.java\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass ValidatorTest {\n    @ParameterizedTest\n    @ValueSource(strings = {\"\", \" \", \"a\".repeat(10001)})\n    void rejectsInvalidInput(String input) {\n        assertThrows(IllegalArgumentException.class, () -> validator.validate(input));\n    }\n}\n```\n\n### 2. Negative \u002F Error Case Testing\nVerify the system behaves correctly under invalid or unexpected conditions.\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FCalculatorTest.java\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass CalculatorTest {\n    @Test\n    void throwsOnDivideByZero() {\n        assertThrows(ArithmeticException.class, () -> calculator.divide(10, 0));\n    }\n}\n```\n\n### 3. State-Based Testing\nFor objects with internal state, verify transitions between states are correct and irreversible where expected.\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FConnectionTest.java\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass ConnectionTest {\n    @Test\n    void stateTransitionsAreCorrect() {\n        Connection conn = new Connection();\n        assertEquals(State.CLOSED, conn.getState());\n        conn.open();\n        assertEquals(State.OPEN, conn.getState());\n        conn.close();\n        assertEquals(State.CLOSED, conn.getState());\n        \u002F\u002F Idempotency\n        conn.close();\n        assertEquals(State.CLOSED, conn.getState());\n    }\n}\n```\n\n## Common Test Smells to Avoid\n\n- **Assertion Roulette**: Many assertions without descriptive messages, making failure diagnosis ambiguous. *Fix: Use `assertAll(\"description\", () -> ...)` or `assertThat(...).as(\"description\").*`.*\n- **Happy Path Bias**: Only testing successful scenarios, leaving error paths uncovered. *Fix: Ensure `assertThrows` for exceptions, test `null`\u002Fempty\u002Fout-of-range inputs.*\n- **Mock Overdose**: Too many mocks hiding integration issues or coupling tests to implementation. *Fix: Mock only at architectural boundaries (DB, HTTP), not internal collaborators.*\n- **The Free Ride**: Tests that execute code but assert nothing meaningful. *Fix: Every test must have at least one specific, falsifiable assertion.*\n- **Obscure Test**: Long, complex test methods with no clear arrange\u002Fact\u002Fassert structure. *Fix: Follow AAA (Arrange-Act-Assert), use `@DisplayName` for intent.*\n- **Flaky Test**: Non-deterministic results from time, order, or shared state. *Fix: Use `@TempDir`, `Clock.fixed()`, `@TestMethodOrder` to guarantee determinism.*\n\n## Best Practices\n\n### DO\n\n- Keep tests deterministic and isolated\n- Use constructor injection for testable components\n- Use `@Nested` for hierarchical test organization\n- Separate unit vs integration tests with `@Tag` or separate source sets\n- Use `assertAll()` for grouped assertions\n- Use `@TempDir` for temporary files\n- Prefer `AssertJ` for fluent, readable assertions\n\n### DON'T\n\n- Don't use `Thread.sleep()` for synchronization\n- Don't depend on real time or network in unit tests\n- Don't use `PowerMock` — refactor code instead\n- Don't mock value objects or simple data classes\n- Don't test private methods directly\n- Don't write slow tests that depend on Spring context for unit tests\n\n### Common Pitfalls\n\n- **Leaking test state** → Reset mocks with `@MockitoSettings` or `@BeforeEach`.\n- **Relying on wall clock** → Inject `Clock` and use `Clock.fixed()`.\n- **Flaky async tests** → Use `awaitility` with bounded waits.\n- **Heavy Spring context** → Use `@WebMvcTest` or slice tests instead of full context.\n- **Over-mocking** → Prefer real implementations for value objects and pure functions.\n- **Missing CI coverage gates** → Add JaCoCo with `haltonFailure` and minimum coverage.\n- **Slow parametrized tests** → Limit `@MethodSource` to representative cases in CI.\n\n## Optional Appendix: Advanced Testing\n\n### Testcontainers (Integration Tests)\n\n```java\nimport org.junit.jupiter.api.Test;\nimport org.testcontainers.junit.jupiter.*;\n\n@Testcontainers\nclass DatabaseTest {\n    @Container\n    static PostgreSQLContainer\u003C?> postgres = new PostgreSQLContainer\u003C>(\"postgres:16\");\n\n    @Test\n    void connectsToDatabase() {\n        \u002F\u002F postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword()\n    }\n}\n```\n\n### ArchUnit (Architecture Tests)\n\n```java\nimport com.tngtech.archunit.junit.AnalyzeClasses;\nimport com.tngtech.archunit.junit.ArchTest;\nimport com.tngtech.archunit.lang.ArchRule;\nimport static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;\n\n@AnalyzeClasses(packages = \"com.example\")\nclass ArchitectureTest {\n    @ArchTest\n    static final ArchRule services_should_not_depend_on_controllers =\n        noClasses().that().resideInAPackage(\"..service..\")\n            .should().dependOnClassesThat()\n            .resideInAPackage(\"..controller..\");\n}\n```\n\n### Spring Boot Test Slices\n\nAvoid slow `@SpringBootTest` for unit tests. Use targeted slices that only load the relevant Spring context:\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FUserControllerTest.java\nimport org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;\nimport org.springframework.test.web.servlet.MockMvc;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\n@WebMvcTest(UserController.class)\nclass UserControllerTest {\n    @Autowired\n    private MockMvc mockMvc;\n\n    @MockBean\n    private UserService userService;\n\n    @Test\n    void returnsUser() throws Exception {\n        when(userService.findById(1L)).thenReturn(new User(\"alice\"));\n\n        mockMvc.perform(get(\"\u002Fusers\u002F1\"))\n            .andExpect(status().isOk())\n            .andExpect(jsonPath(\"$.name\").value(\"alice\"));\n    }\n}\n```\n\n| Slice Annotation | Loads | Use For |\n|------------------|-------|---------|\n| `@WebMvcTest` | Web layer only | Controller tests |\n| `@DataJpaTest` | JPA + datasource | Repository tests |\n| `@RestClientTest` | REST client + Jackson | `RestTemplate`\u002F`WebClient` tests |\n| `@JsonTest` | Jackson\u002FGson only | JSON serialization tests |\n| `@SpringBootTest` | Full context | Integration \u002F E2E tests only |\n\n> **Performance:** Slices typically start 10-100x faster than full `@SpringBootTest`.\n\n## Alternatives to JUnit 5\n\n- **TestNG**: group-based test configuration, less common now.\n- **Spock**: Groovy-based, expressive specification syntax.\n- **Kotest**: Kotlin-native, property-based testing built in.\n\n### Mutation Testing (PIT)\n\nMutation testing verifies that your tests actually catch bugs by introducing small code mutations (e.g., changing `+` to `-`, `>` to `>=`) and checking if tests fail. High mutation score = tests catch real bugs.\n\n```xml\n\u003C!-- pom.xml: pitest-maven plugin -->\n\u003Cplugin>\n    \u003CgroupId>org.pitest\u003C\u002FgroupId>\n    \u003CartifactId>pitest-maven\u003C\u002FartifactId>\n    \u003Cversion>1.17.0\u003C\u002Fversion>\n    \u003Cconfiguration>\n        \u003CtargetClasses>com.example.*\u003C\u002FtargetClasses>\n    \u003C\u002Fconfiguration>\n\u003C\u002Fplugin>\n```\n\n```bash\nmvn org.pitest:pitest-maven:mutationCoverage\n# Report: target\u002Fpit-reports\u002Findex.html\n```\n\n> **Target**: ≥80% mutation score for business logic. Focus on uncovered mutations, not just line coverage.\n","# Java Testing (Agent Skill)\n\nAgent-focused testing workflow for modern Java (17+) using JUnit 5 with Mockito, the industry-standard combination for Java testing.\n\n## When to Use\n\n- Writing new Java tests or fixing existing tests\n- Designing unit\u002Fintegration test coverage for Java components\n- Adding test coverage, CI gating, or regression protection\n- Configuring JUnit 5 \u002F Mockito workflows for consistent execution\n- Investigating test failures or flaky behavior\n- Testing Spring Boot, REST APIs, or library code\n\n### When NOT to Use\n\n- Implementing new product features without test changes\n- Large-scale refactors unrelated to test coverage or failures\n- Performance tuning without test regressions to validate\n- Non-Java projects or non-test tasks\n\n### Stop Conditions — When to ASK instead of generating\n\nBefore generating any test code, STOP and ask the user if:\n- The code under test has **zero testability** (no DI, global state, static method chains, no interfaces)\n- **No test framework** is detected in the project (no JUnit\u002FMockito in pom.xml or build.gradle)\n- The request is **ambiguous**: \"test my app\" or \"add tests\" without specifying files or components\n- A **required test dependency** (JUnit, Mockito, AssertJ) is not installed\n- The test would require **secrets, real credentials, or production database access**\n\n## Core Concepts\n\n- **TDD loop**: red → green → refactor (tests first, minimal fix, then cleanups).\n- **Isolation**: prefer constructor injection and mocks over global state.\n- **Test layout**: `src\u002Ftest\u002Fjava\u002F` with same package structure as source, `src\u002Ftest\u002Fresources\u002F`.\n- **Mocks vs stubs**: Mockito mocks for behavior verification, stubs for state.\n- **Parametrized tests**: `@ParameterizedTest` with `@ValueSource`, `@CsvSource`, `@MethodSource`.\n- **CI signal**: run subset first, then full suite with `--fail-if-no-tests`.\n\n## Agent Execution Protocol\n\nWhen this skill is activated, follow this exact execution sequence:\n\n### Phase 1: Context Analysis\nBefore writing any test code:\n1. Identify the Java version (17+) and build tool (Maven\u002FGradle).\n2. Check for existing test files (`src\u002Ftest\u002Fjava\u002F`) to infer package structure and style (JUnit 5, AssertJ, Mockito).\n3. Analyze the code under test: identify public API surface, side effects, Spring annotations, and concurrency.\n4. Determine request type: (a) new tests, (b) fixing broken tests, (c) coverage improvement, (d) flaky test diagnosis.\n\n### Phase 2: Strategy Selection\nBased on Phase 1, select the appropriate strategy:\n- **Pure functions \u002F value objects**: unit tests only, no mocks needed. Use `@ParameterizedTest` with `@CsvSource`.\n- **External dependencies (DB, HTTP, filesystem)**: use Mockito; prefer Testcontainers for real DB integration tests.\n- **Spring Boot components**: use Test Slices (`@WebMvcTest`, `@DataJpaTest`, `@RestClientTest`) instead of heavy `@SpringBootTest`.\n- **Async code**: use `CompletableFuture` testing or Awaitility for bounded waits.\n\n### Decision Tree (Complex Scenarios)\n\n```text\nQ1: External I\u002FO (DB, HTTP, filesystem, network)?\n  → Yes → Q2\n  → No  → Pure unit test, no mocks needed\n\nQ2: Mockable at code boundary (@Inject\u002F@Autowired, constructor DI, interface)?\n  → Yes → Mock at boundary (Mockito @Mock + @InjectMocks)\n  → No  → Q3\n\nQ3: Needs real behavior verification (SQL, JPA, transactions)?\n  → Yes → @DataJpaTest \u002F Testcontainers (real DB in Docker)\n  → No  → Use fake\u002Fstub implementation\n\nQ4: Async\u002Fconcurrent (CompletableFuture, @Async, reactive)?\n  → Yes → Awaitility for bounded waits, StepVerifier for reactive\n  → No  → Synchronous test sufficient\n\nQ5: Spring MVC\u002FREST controller?\n  → Yes → @WebMvcTest with MockMvc (10-100x faster than @SpringBootTest)\n  → No  → Standard unit test with JUnit 5\n```\n\n### Phase 3: Test Case Design\nApply systematic test design before coding:\n1. **Happy path**: normal input → expected output.\n2. **Boundary values**: empty collections, `null`, `Optional.empty()`, max integer\u002Fstring length.\n3. **Error cases**: exceptions (`assertThrows`), validation errors, 4xx\u002F5xx responses.\n4. **State & concurrency**: mutable shared state, thread-safety, transaction rollback.\n\n### Phase 4: Code Generation\nOutput tests in this exact structure:\n```\n#### Test Strategy Summary\n- [ ] Scope: Unit \u002F Integration \u002F E2E\n- [ ] Framework: JUnit 5 + Mockito + AssertJ\n- [ ] Mocking: Yes\u002FNo — Mockito \u002F Testcontainers\n- [ ] Edge cases covered: \u003Clist>\n\n#### Generated Test Code\n\u003Cfile_path>\n\u003Ccode>\n\n#### Running Commands\n```bash\n# Maven\nmvn test -Dtest=ClassName#methodName\n# Gradle\n.\u002Fgradlew test --tests \"ClassName.methodName\"\n```\n\n#### Coverage Impact (if applicable)\n- Estimated coverage change: \u003CX%>\n- Command to verify: `mvn test jacoco:report` \u002F `.\u002Fgradlew test jacocoTestReport`\n```\n\n### Phase 5: Verification Checklist\nBefore completing, verify:\n- [ ] Tests compile without errors (`mvn test-compile` \u002F `.\u002Fgradlew compileTestJava`).\n- [ ] Each test has exactly one logical assertion focus (use `assertAll` for grouped assertions).\n- [ ] Mocks are reset between tests (`@MockitoSettings` or `@BeforeEach`).\n- [ ] No `Thread.sleep` used for synchronization (use Awaitility).\n- [ ] Flaky test guardrails applied (`@TempDir`, deterministic data, `@Tag` separation).\n\n## TDD Workflow\n\nFollow the RED → GREEN → REFACTOR loop:\n\n1. **RED**: write a failing test that captures the new behavior\n2. **GREEN**: implement the smallest change to pass\n3. **REFACTOR**: clean up while tests stay green\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FAddTest.java\nclass AddTest {\n    @Test \u002F\u002F RED\n    void addsTwoNumbers() {\n        assertEquals(5, Add.add(2, 3));\n    }\n}\n\n\u002F\u002F src\u002Fmain\u002Fjava\u002Fcom\u002Fexample\u002FAdd.java\npublic class Add { \u002F\u002F GREEN\n    public static int add(int a, int b) {\n        return a + b;\n    }\n}\n\n\u002F\u002F REFACTOR: simplify\u002Frename once tests pass\n```\n\n## Code Examples\n\n### Basic Unit Test (JUnit 5)\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FCalculatorTest.java\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass CalculatorTest {\n    @Test\n    void addsTwoNumbers() {\n        assertEquals(5, Calculator.add(2, 3));\n    }\n}\n```\n\n### Lifecycle (JUnit 5)\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FUserStoreTest.java\nimport org.junit.jupiter.api.*;\nimport java.util.*;\n\nclass UserStoreTest {\n    private UserStore store;\n\n    @BeforeEach\n    void setUp() {\n        store = new UserStore();\n        store.seed(List.of(new User(\"alice\"), new User(\"bob\")));\n    }\n\n    @Test\n    void findsExistingUser() {\n        Optional\u003CUser> user = store.find(\"alice\");\n        assertTrue(user.isPresent());\n        assertEquals(\"alice\", user.get().name());\n    }\n}\n```\n\n### Mocking (Mockito)\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FNotifierTest.java\nimport org.junit.jupiter.api.Test;\nimport org.mockito.Mockito;\nimport static org.mockito.Mockito.*;\n\nclass NotifierTest {\n    @Test\n    void sendsNotifications() {\n        Notifier notifier = mock(Notifier.class);\n        Service service = new Service(notifier);\n\n        service.publish(\"hello\");\n\n        verify(notifier, times(1)).send(\"hello\");\n    }\n}\n```\n\n### Mockito with Annotations\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FOrderServiceTest.java\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.*;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport static org.mockito.Mockito.*;\nimport static org.junit.jupiter.api.Assertions.*;\n\n@ExtendWith(MockitoExtension.class)\nclass OrderServiceTest {\n    @Mock\n    private PaymentGateway paymentGateway;\n\n    @InjectMocks\n    private OrderService orderService;\n\n    @Test\n    void processesPayment() {\n        when(paymentGateway.charge(anyDouble())).thenReturn(true);\n\n        boolean result = orderService.processOrder(100.0);\n\n        assertTrue(result);\n        verify(paymentGateway).charge(100.0);\n    }\n}\n```\n\n### Parametrized Test\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FMathTest.java\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.*;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass MathTest {\n    @ParameterizedTest\n    @CsvSource({\n        \"1, 2, 3\",\n        \"-1, 1, 0\",\n        \"0, 0, 0\",\n        \"100, 200, 300\"\n    })\n    void addsNumbers(int a, int b, int expected) {\n        assertEquals(expected, Math.add(a, b));\n    }\n}\n```\n\n### Exception Testing\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FValidationTest.java\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass ValidationTest {\n    @Test\n    void throwsOnNegativeAge() {\n        assertThrows(IllegalArgumentException.class,\n            () -> Validator.validateAge(-1));\n    }\n\n    @Test\n    void throwsWithMessage() {\n        Exception ex = assertThrows(IllegalArgumentException.class,\n            () -> Validator.validateAge(-1));\n        assertTrue(ex.getMessage().contains(\"non-negative\"));\n    }\n}\n```\n\n### AssertJ Fluent Assertions\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FUserTest.java\nimport org.junit.jupiter.api.Test;\nimport static org.assertj.core.api.Assertions.*;\n\nclass UserTest {\n    @Test\n    void userHasCorrectProperties() {\n        User user = new User(\"alice\", \"alice@example.com\");\n\n        assertThat(user.name()).isEqualTo(\"alice\");\n        assertThat(user.email()).endsWith(\"@example.com\");\n        assertThat(user.roles()).contains(\"user\");\n    }\n}\n```\n\n## Build Configuration\n\n### Maven\n\n```xml\n\u003C!-- pom.xml -->\n\u003Cdependencies>\n    \u003Cdependency>\n        \u003CgroupId>org.junit.jupiter\u003C\u002FgroupId>\n        \u003CartifactId>junit-jupiter\u003C\u002FartifactId>\n        \u003Cversion>5.12.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n    \u003Cdependency>\n        \u003CgroupId>org.mockito\u003C\u002FgroupId>\n        \u003CartifactId>mockito-core\u003C\u002FartifactId>\n        \u003Cversion>5.16.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n    \u003Cdependency>\n        \u003CgroupId>org.assertj\u003C\u002FgroupId>\n        \u003CartifactId>assertj-core\u003C\u002FartifactId>\n        \u003Cversion>3.27.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n    \u003C!-- Integration testing -->\n    \u003Cdependency>\n        \u003CgroupId>org.testcontainers\u003C\u002FgroupId>\n        \u003CartifactId>testcontainers\u003C\u002FartifactId>\n        \u003Cversion>1.20.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n    \u003Cdependency>\n        \u003CgroupId>org.awaitility\u003C\u002FgroupId>\n        \u003CartifactId>awaitility\u003C\u002FartifactId>\n        \u003Cversion>4.2.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n\u003C\u002Fdependencies>\n\n\u003Cbuild>\n    \u003Cplugins>\n        \u003Cplugin>\n            \u003CgroupId>org.apache.maven.plugins\u003C\u002FgroupId>\n            \u003CartifactId>maven-surefire-plugin\u003C\u002FartifactId>\n            \u003Cversion>3.2.5\u003C\u002Fversion>\n        \u003C\u002Fplugin>\n    \u003C\u002Fplugins>\n\u003C\u002Fbuild>\n```\n\n### Gradle\n\n```kotlin\n\u002F\u002F build.gradle.kts\ndependencies {\n    testImplementation(\"org.junit.jupiter:junit-jupiter:5.12.0\")\n    testImplementation(\"org.mockito:mockito-core:5.16.0\")\n    testImplementation(\"org.assertj:assertj-core:3.27.0\")\n    testImplementation(\"org.testcontainers:testcontainers:1.20.0\")\n    testImplementation(\"org.awaitility:awaitility:4.2.0\")\n}\n\ntasks.test {\n    useJUnitPlatform()\n    testLogging {\n        events(\"passed\", \"skipped\", \"failed\")\n    }\n}\n```\n\n## Running Tests\n\n```bash\nmvn test                           # run all tests\nmvn test -Dtest=CalculatorTest     # run single test class\nmvn test -Dtest=CalculatorTest#addsTwoNumbers  # run single method\nmvn test -Dtest=\"*Test\"            # wildcard pattern\nmvn test -pl module-name           # run tests in a module\n\n.\u002Fgradlew test                     # run all tests\n.\u002Fgradlew test --tests \"*CalculatorTest*\"  # filter by pattern\n.\u002Fgradlew test --tests \"*CalculatorTest.addsTwoNumbers\"  # single test\n.\u002Fgradlew test --info              # verbose output\n```\n\n## Debugging Failures\n\n1. Re-run the single failing test with `@Tag` filtering or `--tests` pattern.\n2. Pass `-X` to Maven or `--stacktrace` to Gradle for full stack traces.\n3. Add breakpoints and run with debugger (`mvn test -Dmaven.surefire.debug`).\n4. Use `System.out` (captured by surefire by default).\n5. Expand to full suite once the root cause is fixed.\n\n## Coverage\n\n### JaCoCo (Maven)\n\n```xml\n\u003Cplugin>\n    \u003CgroupId>org.jacoco\u003C\u002FgroupId>\n    \u003CartifactId>jacoco-maven-plugin\u003C\u002FartifactId>\n    \u003Cversion>0.8.12\u003C\u002Fversion>\n    \u003Cexecutions>\n        \u003Cexecution>\n            \u003Cgoals>\u003Cgoal>prepare-agent\u003C\u002Fgoal>\u003C\u002Fgoals>\n        \u003C\u002Fexecution>\n        \u003Cexecution>\n            \u003Cid>report\u003C\u002Fid>\n            \u003Cphase>test\u003C\u002Fphase>\n            \u003Cgoals>\u003Cgoal>report\u003C\u002Fgoal>\u003C\u002Fgoals>\n        \u003C\u002Fexecution>\n    \u003C\u002Fexecutions>\n\u003C\u002Fplugin>\n```\n\n### JaCoCo (Gradle)\n\n```kotlin\nplugins {\n    id(\"jacoco\")\n}\n\ntasks.jacocoTestReport {\n    dependsOn(tasks.test)\n    reports {\n        xml.required.set(true)\n        html.required.set(true)\n    }\n}\n```\n\n```bash\nmvn test jacoco:report\n.\u002Fgradlew test jacocoTestReport\n```\n\n## CI\u002FCD Integration (GitHub Actions)\n\n```yaml\n# .github\u002Fworkflows\u002Ftest.yml\nname: Test\non: [push, pull_request]\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: actions\u002Fsetup-java@v4\n        with: { java-version: '21', distribution: 'temurin' }\n      - run: mvn test jacoco:report\n      - uses: actions\u002Fupload-artifact@v4\n        if: always()\n        with: { name: test-results, path: target\u002Fsurefire-reports\u002F*.xml }\n```\n\n## Flaky Tests Guardrails\n\n- Never use `Thread.sleep()` for synchronization; use `awaitility`.\n- Make temp directories unique per test with `@TempDir`.\n- Avoid real time, network, or filesystem dependencies in unit tests.\n- Use `java.time.Clock` injection for time-dependent code.\n- Use `awaitility` for async assertions with timeout.\n\n## Test Case Design Patterns\n\nApply these systematic patterns before writing test code to ensure robust coverage beyond the happy path.\n\n### 1. Boundary Value Analysis\nTest at the edges of valid input domains: empty, zero, maximum, null, and just-outside valid ranges.\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FValidatorTest.java\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass ValidatorTest {\n    @ParameterizedTest\n    @ValueSource(strings = {\"\", \" \", \"a\".repeat(10001)})\n    void rejectsInvalidInput(String input) {\n        assertThrows(IllegalArgumentException.class, () -> validator.validate(input));\n    }\n}\n```\n\n### 2. Negative \u002F Error Case Testing\nVerify the system behaves correctly under invalid or unexpected conditions.\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FCalculatorTest.java\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass CalculatorTest {\n    @Test\n    void throwsOnDivideByZero() {\n        assertThrows(ArithmeticException.class, () -> calculator.divide(10, 0));\n    }\n}\n```\n\n### 3. State-Based Testing\nFor objects with internal state, verify transitions between states are correct and irreversible where expected.\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FConnectionTest.java\nimport org.junit.jupiter.api.Test;\nimport static org.junit.jupiter.api.Assertions.*;\n\nclass ConnectionTest {\n    @Test\n    void stateTransitionsAreCorrect() {\n        Connection conn = new Connection();\n        assertEquals(State.CLOSED, conn.getState());\n        conn.open();\n        assertEquals(State.OPEN, conn.getState());\n        conn.close();\n        assertEquals(State.CLOSED, conn.getState());\n        \u002F\u002F Idempotency\n        conn.close();\n        assertEquals(State.CLOSED, conn.getState());\n    }\n}\n```\n\n## Common Test Smells to Avoid\n\n- **Assertion Roulette**: Many assertions without descriptive messages, making failure diagnosis ambiguous. *Fix: Use `assertAll(\"description\", () -> ...)` or `assertThat(...).as(\"description\").*`.*\n- **Happy Path Bias**: Only testing successful scenarios, leaving error paths uncovered. *Fix: Ensure `assertThrows` for exceptions, test `null`\u002Fempty\u002Fout-of-range inputs.*\n- **Mock Overdose**: Too many mocks hiding integration issues or coupling tests to implementation. *Fix: Mock only at architectural boundaries (DB, HTTP), not internal collaborators.*\n- **The Free Ride**: Tests that execute code but assert nothing meaningful. *Fix: Every test must have at least one specific, falsifiable assertion.*\n- **Obscure Test**: Long, complex test methods with no clear arrange\u002Fact\u002Fassert structure. *Fix: Follow AAA (Arrange-Act-Assert), use `@DisplayName` for intent.*\n- **Flaky Test**: Non-deterministic results from time, order, or shared state. *Fix: Use `@TempDir`, `Clock.fixed()`, `@TestMethodOrder` to guarantee determinism.*\n\n## Best Practices\n\n### DO\n\n- Keep tests deterministic and isolated\n- Use constructor injection for testable components\n- Use `@Nested` for hierarchical test organization\n- Separate unit vs integration tests with `@Tag` or separate source sets\n- Use `assertAll()` for grouped assertions\n- Use `@TempDir` for temporary files\n- Prefer `AssertJ` for fluent, readable assertions\n\n### DON'T\n\n- Don't use `Thread.sleep()` for synchronization\n- Don't depend on real time or network in unit tests\n- Don't use `PowerMock` — refactor code instead\n- Don't mock value objects or simple data classes\n- Don't test private methods directly\n- Don't write slow tests that depend on Spring context for unit tests\n\n### Common Pitfalls\n\n- **Leaking test state** → Reset mocks with `@MockitoSettings` or `@BeforeEach`.\n- **Relying on wall clock** → Inject `Clock` and use `Clock.fixed()`.\n- **Flaky async tests** → Use `awaitility` with bounded waits.\n- **Heavy Spring context** → Use `@WebMvcTest` or slice tests instead of full context.\n- **Over-mocking** → Prefer real implementations for value objects and pure functions.\n- **Missing CI coverage gates** → Add JaCoCo with `haltonFailure` and minimum coverage.\n- **Slow parametrized tests** → Limit `@MethodSource` to representative cases in CI.\n\n## Optional Appendix: Advanced Testing\n\n### Testcontainers (Integration Tests)\n\n```java\nimport org.junit.jupiter.api.Test;\nimport org.testcontainers.junit.jupiter.*;\n\n@Testcontainers\nclass DatabaseTest {\n    @Container\n    static PostgreSQLContainer\u003C?> postgres = new PostgreSQLContainer\u003C>(\"postgres:16\");\n\n    @Test\n    void connectsToDatabase() {\n        \u002F\u002F postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword()\n    }\n}\n```\n\n### ArchUnit (Architecture Tests)\n\n```java\nimport com.tngtech.archunit.junit.AnalyzeClasses;\nimport com.tngtech.archunit.junit.ArchTest;\nimport com.tngtech.archunit.lang.ArchRule;\nimport static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;\n\n@AnalyzeClasses(packages = \"com.example\")\nclass ArchitectureTest {\n    @ArchTest\n    static final ArchRule services_should_not_depend_on_controllers =\n        noClasses().that().resideInAPackage(\"..service..\")\n            .should().dependOnClassesThat()\n            .resideInAPackage(\"..controller..\");\n}\n```\n\n### Spring Boot Test Slices\n\nAvoid slow `@SpringBootTest` for unit tests. Use targeted slices that only load the relevant Spring context:\n\n```java\n\u002F\u002F src\u002Ftest\u002Fjava\u002Fcom\u002Fexample\u002FUserControllerTest.java\nimport org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;\nimport org.springframework.test.web.servlet.MockMvc;\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;\n\n@WebMvcTest(UserController.class)\nclass UserControllerTest {\n    @Autowired\n    private MockMvc mockMvc;\n\n    @MockBean\n    private UserService userService;\n\n    @Test\n    void returnsUser() throws Exception {\n        when(userService.findById(1L)).thenReturn(new User(\"alice\"));\n\n        mockMvc.perform(get(\"\u002Fusers\u002F1\"))\n            .andExpect(status().isOk())\n            .andExpect(jsonPath(\"$.name\").value(\"alice\"));\n    }\n}\n```\n\n| Slice Annotation | Loads | Use For |\n|------------------|-------|---------|\n| `@WebMvcTest` | Web layer only | Controller tests |\n| `@DataJpaTest` | JPA + datasource | Repository tests |\n| `@RestClientTest` | REST client + Jackson | `RestTemplate`\u002F`WebClient` tests |\n| `@JsonTest` | Jackson\u002FGson only | JSON serialization tests |\n| `@SpringBootTest` | Full context | Integration \u002F E2E tests only |\n\n> **Performance:** Slices typically start 10-100x faster than full `@SpringBootTest`.\n\n## Alternatives to JUnit 5\n\n- **TestNG**: group-based test configuration, less common now.\n- **Spock**: Groovy-based, expressive specification syntax.\n- **Kotest**: Kotlin-native, property-based testing built in.\n\n### Mutation Testing (PIT)\n\nMutation testing verifies that your tests actually catch bugs by introducing small code mutations (e.g., changing `+` to `-`, `>` to `>=`) and checking if tests fail. High mutation score = tests catch real bugs.\n\n```xml\n\u003C!-- pom.xml: pitest-maven plugin -->\n\u003Cplugin>\n    \u003CgroupId>org.pitest\u003C\u002FgroupId>\n    \u003CartifactId>pitest-maven\u003C\u002FartifactId>\n    \u003Cversion>1.17.0\u003C\u002Fversion>\n    \u003Cconfiguration>\n        \u003CtargetClasses>com.example.*\u003C\u002FtargetClasses>\n    \u003C\u002Fconfiguration>\n\u003C\u002Fplugin>\n```\n\n```bash\nmvn org.pitest:pitest-maven:mutationCoverage\n# Report: target\u002Fpit-reports\u002Findex.html\n```\n\n> **Target**: ≥80% mutation score for business logic. Focus on uncovered mutations, not just line coverage.\n",{"name":5,"metadata":24,"description":27,"compatibility":28},{"author":25,"source":26},"github-spec-kit","templates\u002Fpassive-skills\u002Fjava-testing\u002FSKILL.md","Use ONLY when user explicitly requests Java test code, JUnit\u002FMockito\u002FAssertJ configuration, test failure diagnosis, or coverage improvement for Java projects.\nTriggers: \"write test java\", \"JUnit\", \"Mockito\", \"AssertJ\", \"coverage java\", \"flaky test java\", \"Spring Boot test\", \"Testcontainers\", \"test slice\", \"jacoco\".\nDo NOT activate for: general Java coding without test context, Maven\u002FGradle build configuration outside test scope, Spring bean wiring, or non-test dependency issues. ","Requires spec-kit project structure with .specify\u002F directory","main","021f5cbe02157229460a577e9a585f2da6d78cb0",".codex\u002Fskills\u002Fjava-testing","https:\u002F\u002Fgithub.com\u002Fchenziyang110\u002Flaunchdeck","https:\u002F\u002Fgithub.com\u002Fchenziyang110","2026-08-07 18:10:54",{"source":8,"skillName":5,"commands":36,"prompt":46,"zipAvailable":47},[37,40,43],{"tool":38,"command":39},"npx","npx skills add chenziyang110\u002Flaunchdeck --skill java-testing",{"tool":41,"command":42},"bunx","bunx skills add chenziyang110\u002Flaunchdeck --skill java-testing",{"tool":44,"command":45},"pnpm","pnpm dlx skills add chenziyang110\u002Flaunchdeck --skill java-testing","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fchenziyang110\u002Flaunchdeck\u002Fblob\u002Fmain\u002F.codex\u002Fskills\u002Fjava-testing\u002FSKILL.md",true,[],[50,57,63,69,76,85],{"skillNo":51,"name":52,"nameZh":6,"description":53,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":54,"categories":55,"keywords":56,"publishedAt":19},"01M7875F3HQ1GF5F6BR36KY3JE","launchdeck-agent","Codex host adapter contract fixture for Launchdeck.","https:\u002F\u002Fgithub.com\u002Fchenziyang110\u002Flaunchdeck\u002Fblob\u002Fmain\u002Ftest\u002Ffixtures\u002Fagent-hosts\u002Fcodex\u002Fskill\u002FSKILL.md",[],[],{"skillNo":58,"name":52,"nameZh":6,"description":59,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":60,"categories":61,"keywords":62,"publishedAt":19},"01M7875DV2DQR33K1NFJHKAMZP","Claude host adapter contract fixture for Launchdeck.","https:\u002F\u002Fgithub.com\u002Fchenziyang110\u002Flaunchdeck\u002Fblob\u002Fmain\u002Ftest\u002Ffixtures\u002Fagent-hosts\u002Fclaude\u002Fskill\u002FSKILL.md",[],[],{"skillNo":64,"name":52,"nameZh":6,"description":65,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":66,"categories":67,"keywords":68,"publishedAt":19},"01M78759JQ7A6FQ8KDQTNYS3X9","Use this skill aggressively for local project lifecycle operations even when the user does not mention Launchdeck: start this project, run the dev server, dev, build, test, restart service, stop service, show logs, inspect ports, port is occupied, clean cache; and when the user explicitly asks to configure this local project for Launchdeck or create or generate a project-adapted `.launchdeck.yml`, including 为这个项目生成 Launchdeck 配置. Do not use for ordinary code edits, docs-only work, general explanations, API design discussion, or production deployment unless the user also asks to operate or configure a local project or service.","https:\u002F\u002Fgithub.com\u002Fchenziyang110\u002Flaunchdeck\u002Fblob\u002Fmain\u002Fagent\u002Finstaller-payload\u002Fskill\u002Flaunchdeck-agent\u002FSKILL.md",[],[],{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":75,"publishedAt":19},"01M78753ZJQ7G2CZT1W1G8WRJV","zig-testing","Use ONLY when user explicitly requests Zig test code, built-in test runner configuration,\ntest failure diagnosis, or coverage for Zig projects.\n\nTriggers: \"write test zig\", \"zig test\", \"testing.allocator\", \"zig coverage\",\n\"comptime test\", \"zig expectError\", \"zig snapshot test\", \"zig fuzz\".\n\nDo NOT activate for: general Zig coding without test context, build.zig configuration\nfor non-test targets, Zig compiler errors, or Zig build system setup.","https:\u002F\u002Fgithub.com\u002Fchenziyang110\u002Flaunchdeck\u002Fblob\u002Fmain\u002F.specify\u002Ftemplates\u002Fpassive-skills\u002Fzig-testing\u002FSKILL.md",[],[],{"skillNo":77,"name":78,"nameZh":6,"description":79,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":80,"licenseFileUrl":81,"sourceHtmlUrl":82,"categories":83,"keywords":84,"publishedAt":19},"01M78750JXY4Z5D5W1EX3PQ31Z","webapp-testing","Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fchenziyang110\u002Flaunchdeck\u002Fblob\u002Fmain\u002F.specify\u002Ftemplates\u002Fpassive-skills\u002Fwebapp-testing\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fchenziyang110\u002Flaunchdeck\u002Fblob\u002Fmain\u002F.specify\u002Ftemplates\u002Fpassive-skills\u002Fwebapp-testing\u002FSKILL.md",[],[],{"skillNo":86,"name":87,"nameZh":6,"description":88,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":91,"publishedAt":19},"01M7874X2T83EQQFRCEY5SJJVC","verification-before-completion","Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always","https:\u002F\u002Fgithub.com\u002Fchenziyang110\u002Flaunchdeck\u002Fblob\u002Fmain\u002F.specify\u002Ftemplates\u002Fpassive-skills\u002Fverification-before-completion\u002FSKILL.md",[],[]]