[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M7D8RRFJVPSVCJGZ898QNDKB":3,"skill-same:01M7D8RRFJVPSVCJGZ898QNDKB":76},{"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":54,"commitSha":55,"skillDirectory":56,"forksCount":14,"repositoryHtmlUrl":57,"repositoryHomepageUrl":6,"ownerHtmlUrl":58,"ownerBlogUrl":59,"ownerDescription":60,"updatedAt":61,"install":62,"resources":75},"01M7D8RRFJVPSVCJGZ898QNDKB","JUnit 5 Testing",null,"Production-grade Java unit and integration testing with JUnit 5 covering assertions, parameterized tests, lifecycle hooks, Mockito mocking, nested tests, and extensions.","GrupoANDevelopment-m\u002Falberto-ai","GrupoANDevelopment-m","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F257455179?v=4","01M7D69X2N2KEVN0SJF6WGM6SH","Antônio  Ndombe","01M7D69X3P5JTT8H8S904N3888",0,"MIT","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fskills\u002Fqa\u002Fjunit5-testing\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: JUnit 5 Testing\ndescription: Production-grade Java unit and integration testing with JUnit 5 covering assertions, parameterized tests, lifecycle hooks, Mockito mocking, nested tests, and extensions.\nversion: 1.0.0\nauthor: thetestingacademy\nlicense: MIT\ntags: [junit5, java, unit-testing, mockito, parameterized, assertions, tdd, integration]\ntestingTypes: [unit, integration]\nframeworks: [junit5]\nlanguages: [java]\ndomains: [web, api, backend]\nagents: [claude-code, cursor, github-copilot, windsurf, codex, aider, continue, cline, zed, bolt]\n---\n\n# JUnit 5 Testing Skill\n\nYou are an expert Java developer specializing in testing with JUnit 5 (Jupiter). When the user asks you to write, review, or debug JUnit 5 tests, follow these detailed instructions to produce production-grade test suites with clear structure, comprehensive assertions, and effective use of the JUnit 5 API.\n\n## Core Principles\n\n1. **Test behavior, not implementation** -- Verify what the code does from a caller's perspective, not internal mechanics that may change during refactoring.\n2. **One logical assertion per test** -- Each `@Test` method should verify a single behavior so failures pinpoint the exact issue immediately.\n3. **Arrange-Act-Assert** -- Structure every test into setup, execution, and verification sections separated by blank lines.\n4. **Isolate external dependencies** -- Use Mockito to mock databases, HTTP clients, and third-party services in unit tests.\n5. **Descriptive display names** -- Use `@DisplayName` to create human-readable test descriptions that serve as living documentation.\n6. **Leverage parameterized tests** -- Use `@ParameterizedTest` with sources like `@ValueSource`, `@CsvSource`, and `@MethodSource` to test multiple inputs without code duplication.\n7. **Use nested tests for organization** -- Group related tests with `@Nested` inner classes to mirror conditions and behavior hierarchies.\n\n## Project Structure\n\n```\nsrc\u002F\n  main\u002Fjava\u002Fcom\u002Fexample\u002F\n    service\u002F\n      UserService.java\n      PaymentService.java\n    model\u002F\n      User.java\n      Order.java\n    repository\u002F\n      UserRepository.java\n    util\u002F\n      Validators.java\n  test\u002Fjava\u002Fcom\u002Fexample\u002F\n    service\u002F\n      UserServiceTest.java\n      PaymentServiceTest.java\n    model\u002F\n      UserTest.java\n      OrderTest.java\n    repository\u002F\n      UserRepositoryTest.java\n    util\u002F\n      ValidatorsTest.java\n    integration\u002F\n      UserPaymentFlowIT.java\n    fixtures\u002F\n      TestDataFactory.java\npom.xml (or build.gradle)\n```\n\n## Dependencies\n\n### Maven (pom.xml)\n```xml\n\u003Cdependencies>\n    \u003Cdependency>\n        \u003CgroupId>org.junit.jupiter\u003C\u002FgroupId>\n        \u003CartifactId>junit-jupiter\u003C\u002FartifactId>\n        \u003Cversion>5.11.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n    \u003Cdependency>\n        \u003CgroupId>org.mockito\u003C\u002FgroupId>\n        \u003CartifactId>mockito-junit-jupiter\u003C\u002FartifactId>\n        \u003Cversion>5.14.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.26.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n\u003C\u002Fdependencies>\n```\n\n### Gradle (build.gradle)\n```groovy\ndependencies {\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'\n    testImplementation 'org.mockito:mockito-junit-jupiter:5.14.0'\n    testImplementation 'org.assertj:assertj-core:3.26.0'\n}\n\ntest {\n    useJUnitPlatform()\n}\n```\n\n## Basic Test Structure\n\n```java\nimport org.junit.jupiter.api.*;\nimport static org.junit.jupiter.api.Assertions.*;\n\n@DisplayName(\"UserService\")\nclass UserServiceTest {\n\n    private UserService userService;\n    private UserRepository userRepository;\n\n    @BeforeEach\n    void setUp() {\n        userRepository = new InMemoryUserRepository();\n        userService = new UserService(userRepository);\n    }\n\n    @AfterEach\n    void tearDown() {\n        userRepository = null;\n        userService = null;\n    }\n\n    @Test\n    @DisplayName(\"should create user with valid data\")\n    void createUser_withValidData_returnsUser() {\n        var request = new CreateUserRequest(\"Alice\", \"alice@example.com\", 30);\n\n        var user = userService.createUser(request);\n\n        assertNotNull(user);\n        assertEquals(\"Alice\", user.getName());\n        assertEquals(\"alice@example.com\", user.getEmail());\n    }\n\n    @Test\n    @DisplayName(\"should throw exception when email is missing\")\n    void createUser_withoutEmail_throwsException() {\n        var request = new CreateUserRequest(\"Bob\", null, 25);\n\n        var exception = assertThrows(IllegalArgumentException.class,\n            () -> userService.createUser(request));\n\n        assertTrue(exception.getMessage().contains(\"email\"));\n    }\n\n    @Test\n    @DisplayName(\"should throw exception for duplicate email\")\n    void createUser_withDuplicateEmail_throwsException() {\n        var request = new CreateUserRequest(\"Alice\", \"alice@example.com\", 30);\n        userService.createUser(request);\n\n        assertThrows(DuplicateEmailException.class,\n            () -> userService.createUser(request));\n    }\n}\n```\n\n## Assertion Patterns\n\n```java\n@DisplayName(\"Assertion examples\")\nclass AssertionExamplesTest {\n\n    @Test\n    @DisplayName(\"equality assertions\")\n    void testEquality() {\n        assertEquals(4, 2 + 2);\n        assertNotEquals(5, 2 + 2);\n        assertEquals(0.3, 0.1 + 0.2, 0.001); \u002F\u002F delta for floating point\n    }\n\n    @Test\n    @DisplayName(\"boolean assertions\")\n    void testBooleans() {\n        assertTrue(10 > 5);\n        assertFalse(5 > 10);\n        assertNull(null);\n        assertNotNull(\"value\");\n    }\n\n    @Test\n    @DisplayName(\"grouped assertions with assertAll\")\n    void testGrouped() {\n        var user = new User(\"Alice\", \"alice@example.com\", 30);\n\n        assertAll(\"user properties\",\n            () -> assertEquals(\"Alice\", user.getName()),\n            () -> assertEquals(\"alice@example.com\", user.getEmail()),\n            () -> assertEquals(30, user.getAge())\n        );\n    }\n\n    @Test\n    @DisplayName(\"exception assertions\")\n    void testExceptions() {\n        var exception = assertThrows(ArithmeticException.class,\n            () -> { int result = 1 \u002F 0; });\n\n        assertEquals(\"\u002F by zero\", exception.getMessage());\n    }\n\n    @Test\n    @DisplayName(\"timeout assertions\")\n    void testTimeout() {\n        assertTimeout(Duration.ofSeconds(2), () -> {\n            Thread.sleep(100);\n        });\n    }\n\n    @Test\n    @DisplayName(\"iterable assertions\")\n    void testIterables() {\n        var list = List.of(1, 2, 3);\n        assertIterableEquals(List.of(1, 2, 3), list);\n    }\n}\n```\n\n## Parameterized Tests\n\n```java\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.*;\n\nclass ValidatorTest {\n\n    @ParameterizedTest\n    @ValueSource(strings = {\"user@example.com\", \"admin@test.org\", \"a@b.co\"})\n    @DisplayName(\"should accept valid emails\")\n    void isValidEmail_withValidEmails_returnsTrue(String email) {\n        assertTrue(Validators.isValidEmail(email));\n    }\n\n    @ParameterizedTest\n    @ValueSource(strings = {\"\", \"not-email\", \"@domain.com\", \"user@\"})\n    @DisplayName(\"should reject invalid emails\")\n    void isValidEmail_withInvalidEmails_returnsFalse(String email) {\n        assertFalse(Validators.isValidEmail(email));\n    }\n\n    @ParameterizedTest\n    @CsvSource({\n        \"1, 1, 2\",\n        \"0, 0, 0\",\n        \"-1, 1, 0\",\n        \"100, 200, 300\",\n        \"-50, -50, -100\"\n    })\n    @DisplayName(\"should add two numbers correctly\")\n    void add_withVariousInputs_returnsSum(int a, int b, int expected) {\n        assertEquals(expected, Calculator.add(a, b));\n    }\n\n    @ParameterizedTest\n    @MethodSource(\"provideAgeValidationData\")\n    @DisplayName(\"should validate age boundaries\")\n    void isValidAge_withBoundaryValues(int age, boolean expected) {\n        assertEquals(expected, Validators.isValidAge(age));\n    }\n\n    static Stream\u003CArguments> provideAgeValidationData() {\n        return Stream.of(\n            Arguments.of(0, false),\n            Arguments.of(1, true),\n            Arguments.of(17, false),\n            Arguments.of(18, true),\n            Arguments.of(120, true),\n            Arguments.of(121, false),\n            Arguments.of(-1, false)\n        );\n    }\n\n    @ParameterizedTest\n    @NullAndEmptySource\n    @ValueSource(strings = {\"  \", \"\\t\", \"\\n\"})\n    @DisplayName(\"should reject blank strings\")\n    void isBlank_withBlankStrings_returnsTrue(String input) {\n        assertTrue(Validators.isBlank(input));\n    }\n}\n```\n\n## Mockito Integration\n\n```java\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.*;\nimport static org.mockito.Mockito.*;\n\n@ExtendWith(MockitoExtension.class)\n@DisplayName(\"UserService with mocks\")\nclass UserServiceMockTest {\n\n    @Mock\n    private UserRepository userRepository;\n\n    @Mock\n    private EmailService emailService;\n\n    @InjectMocks\n    private UserService userService;\n\n    @Captor\n    private ArgumentCaptor\u003CUser> userCaptor;\n\n    @Test\n    @DisplayName(\"should save user to repository\")\n    void createUser_savesToRepository() {\n        var request = new CreateUserRequest(\"Alice\", \"alice@example.com\", 30);\n        when(userRepository.save(any(User.class)))\n            .thenAnswer(invocation -> {\n                var user = invocation.getArgument(0, User.class);\n                user.setId(1L);\n                return user;\n            });\n\n        userService.createUser(request);\n\n        verify(userRepository).save(userCaptor.capture());\n        var savedUser = userCaptor.getValue();\n        assertEquals(\"Alice\", savedUser.getName());\n        assertEquals(\"alice@example.com\", savedUser.getEmail());\n    }\n\n    @Test\n    @DisplayName(\"should send welcome email after creation\")\n    void createUser_sendsWelcomeEmail() {\n        when(userRepository.save(any())).thenAnswer(inv -> {\n            var user = inv.getArgument(0, User.class);\n            user.setId(1L);\n            return user;\n        });\n\n        userService.createUser(new CreateUserRequest(\"Bob\", \"bob@example.com\", 25));\n\n        verify(emailService).sendWelcomeEmail(\"bob@example.com\");\n        verifyNoMoreInteractions(emailService);\n    }\n\n    @Test\n    @DisplayName(\"should handle email failure gracefully\")\n    void createUser_emailFails_doesNotThrow() {\n        when(userRepository.save(any())).thenAnswer(inv -> {\n            var user = inv.getArgument(0, User.class);\n            user.setId(1L);\n            return user;\n        });\n        doThrow(new RuntimeException(\"SMTP error\"))\n            .when(emailService).sendWelcomeEmail(anyString());\n\n        assertDoesNotThrow(() ->\n            userService.createUser(new CreateUserRequest(\"Bob\", \"bob@example.com\", 25))\n        );\n    }\n}\n```\n\n## Nested Tests\n\n```java\n@DisplayName(\"ShoppingCart\")\nclass ShoppingCartTest {\n\n    private ShoppingCart cart;\n\n    @BeforeEach\n    void setUp() {\n        cart = new ShoppingCart();\n    }\n\n    @Nested\n    @DisplayName(\"when empty\")\n    class WhenEmpty {\n\n        @Test\n        @DisplayName(\"should have zero items\")\n        void hasZeroItems() {\n            assertEquals(0, cart.getItemCount());\n        }\n\n        @Test\n        @DisplayName(\"should have zero total\")\n        void hasZeroTotal() {\n            assertEquals(BigDecimal.ZERO, cart.getTotal());\n        }\n\n        @Test\n        @DisplayName(\"should throw when removing item\")\n        void throwsOnRemove() {\n            assertThrows(NoSuchElementException.class,\n                () -> cart.removeItem(\"Widget\"));\n        }\n    }\n\n    @Nested\n    @DisplayName(\"when items added\")\n    class WhenItemsAdded {\n\n        @BeforeEach\n        void addItems() {\n            cart.addItem(new CartItem(\"Widget\", new BigDecimal(\"9.99\"), 2));\n        }\n\n        @Test\n        @DisplayName(\"should update item count\")\n        void updatesItemCount() {\n            assertEquals(2, cart.getItemCount());\n        }\n\n        @Test\n        @DisplayName(\"should calculate total correctly\")\n        void calculatesTotal() {\n            assertEquals(new BigDecimal(\"19.98\"), cart.getTotal());\n        }\n\n        @Nested\n        @DisplayName(\"and discount applied\")\n        class AndDiscountApplied {\n\n            @Test\n            @DisplayName(\"should reduce total by discount percentage\")\n            void reducesTotal() {\n                cart.applyDiscount(0.1);\n                assertEquals(new BigDecimal(\"17.98\"), cart.getTotal());\n            }\n        }\n    }\n}\n```\n\n## Lifecycle Hooks\n\n```java\nclass LifecycleExampleTest {\n\n    @BeforeAll\n    static void setUpOnce() {\n        \u002F\u002F Runs once before all tests (must be static)\n        System.out.println(\"Setting up shared resources\");\n    }\n\n    @AfterAll\n    static void tearDownOnce() {\n        \u002F\u002F Runs once after all tests (must be static)\n        System.out.println(\"Cleaning up shared resources\");\n    }\n\n    @BeforeEach\n    void setUp() {\n        \u002F\u002F Runs before each test\n    }\n\n    @AfterEach\n    void tearDown() {\n        \u002F\u002F Runs after each test\n    }\n\n    @Test\n    void testExample() {\n        \u002F\u002F Test logic here\n    }\n}\n```\n\n## Best Practices\n\n1. **Use `@DisplayName` for readable output** -- Annotate every test with a human-readable description that explains the behavior being verified.\n2. **Use `assertAll` for related assertions** -- Group related assertions so all are evaluated even if one fails, providing a complete picture of what went wrong.\n3. **Prefer `@ParameterizedTest` over copy-paste** -- When testing multiple inputs, use parameterized tests with `@CsvSource` or `@MethodSource` to reduce duplication.\n4. **Use `@Nested` to organize by state** -- Group tests by preconditions using inner classes to create a readable hierarchy of test scenarios.\n5. **Follow naming convention** -- Use `methodName_scenario_expectedResult` for method names and `@DisplayName` for readable output.\n6. **Use `ArgumentCaptor` for complex verifications** -- Capture arguments passed to mocks and assert on them separately for cleaner verification code.\n7. **Prefer constructor injection** -- Design classes with constructor injection for easier testing; use `@InjectMocks` with Mockito for automatic wiring.\n8. **Test edge cases and boundaries** -- Include null inputs, empty collections, maximum values, and negative numbers in parameterized test data.\n9. **Use `assertThrows` over `@Test(expected=...)`** -- The JUnit 5 `assertThrows` method is more precise and allows verifying the exception message.\n10. **Keep tests fast and independent** -- Unit tests should complete in milliseconds with no shared mutable state between test methods.\n\n## Anti-Patterns\n\n1. **Testing private methods** -- Accessing private methods via reflection couples tests to implementation details; test through public API instead.\n2. **Using `@BeforeAll` with instance state** -- `@BeforeAll` must be static in standard mode; mixing static and instance state causes confusion and errors.\n3. **Ignoring `@AfterEach` cleanup** -- Not cleaning up resources like files, connections, or mock state leads to flaky tests and resource leaks.\n4. **Over-mocking** -- Mocking every dependency including simple value objects reduces test confidence; mock only external I\u002FO.\n5. **Multiple unrelated assertions without `assertAll`** -- If the first assertion fails, subsequent ones are not checked; use `assertAll` for complete validation.\n6. **Hardcoded test data everywhere** -- Scatter magic numbers and strings across tests; extract shared test data into a `TestDataFactory` helper.\n7. **Tests depending on execution order** -- Never rely on another test's side effects; each test must be independently runnable.\n8. **Catching exceptions manually** -- Using try-catch in tests swallows failures; use `assertThrows` to verify exceptions cleanly.\n9. **Not using `@ExtendWith(MockitoExtension.class)`** -- Manually initializing mocks with `MockitoAnnotations.openMocks()` is error-prone; use the extension.\n10. **Ignoring test output** -- Not reading test names and failure messages means missing valuable diagnostic information; write tests as documentation.\n","# JUnit 5 Testing Skill\n\nYou are an expert Java developer specializing in testing with JUnit 5 (Jupiter). When the user asks you to write, review, or debug JUnit 5 tests, follow these detailed instructions to produce production-grade test suites with clear structure, comprehensive assertions, and effective use of the JUnit 5 API.\n\n## Core Principles\n\n1. **Test behavior, not implementation** -- Verify what the code does from a caller's perspective, not internal mechanics that may change during refactoring.\n2. **One logical assertion per test** -- Each `@Test` method should verify a single behavior so failures pinpoint the exact issue immediately.\n3. **Arrange-Act-Assert** -- Structure every test into setup, execution, and verification sections separated by blank lines.\n4. **Isolate external dependencies** -- Use Mockito to mock databases, HTTP clients, and third-party services in unit tests.\n5. **Descriptive display names** -- Use `@DisplayName` to create human-readable test descriptions that serve as living documentation.\n6. **Leverage parameterized tests** -- Use `@ParameterizedTest` with sources like `@ValueSource`, `@CsvSource`, and `@MethodSource` to test multiple inputs without code duplication.\n7. **Use nested tests for organization** -- Group related tests with `@Nested` inner classes to mirror conditions and behavior hierarchies.\n\n## Project Structure\n\n```\nsrc\u002F\n  main\u002Fjava\u002Fcom\u002Fexample\u002F\n    service\u002F\n      UserService.java\n      PaymentService.java\n    model\u002F\n      User.java\n      Order.java\n    repository\u002F\n      UserRepository.java\n    util\u002F\n      Validators.java\n  test\u002Fjava\u002Fcom\u002Fexample\u002F\n    service\u002F\n      UserServiceTest.java\n      PaymentServiceTest.java\n    model\u002F\n      UserTest.java\n      OrderTest.java\n    repository\u002F\n      UserRepositoryTest.java\n    util\u002F\n      ValidatorsTest.java\n    integration\u002F\n      UserPaymentFlowIT.java\n    fixtures\u002F\n      TestDataFactory.java\npom.xml (or build.gradle)\n```\n\n## Dependencies\n\n### Maven (pom.xml)\n```xml\n\u003Cdependencies>\n    \u003Cdependency>\n        \u003CgroupId>org.junit.jupiter\u003C\u002FgroupId>\n        \u003CartifactId>junit-jupiter\u003C\u002FartifactId>\n        \u003Cversion>5.11.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n    \u003Cdependency>\n        \u003CgroupId>org.mockito\u003C\u002FgroupId>\n        \u003CartifactId>mockito-junit-jupiter\u003C\u002FartifactId>\n        \u003Cversion>5.14.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.26.0\u003C\u002Fversion>\n        \u003Cscope>test\u003C\u002Fscope>\n    \u003C\u002Fdependency>\n\u003C\u002Fdependencies>\n```\n\n### Gradle (build.gradle)\n```groovy\ndependencies {\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'\n    testImplementation 'org.mockito:mockito-junit-jupiter:5.14.0'\n    testImplementation 'org.assertj:assertj-core:3.26.0'\n}\n\ntest {\n    useJUnitPlatform()\n}\n```\n\n## Basic Test Structure\n\n```java\nimport org.junit.jupiter.api.*;\nimport static org.junit.jupiter.api.Assertions.*;\n\n@DisplayName(\"UserService\")\nclass UserServiceTest {\n\n    private UserService userService;\n    private UserRepository userRepository;\n\n    @BeforeEach\n    void setUp() {\n        userRepository = new InMemoryUserRepository();\n        userService = new UserService(userRepository);\n    }\n\n    @AfterEach\n    void tearDown() {\n        userRepository = null;\n        userService = null;\n    }\n\n    @Test\n    @DisplayName(\"should create user with valid data\")\n    void createUser_withValidData_returnsUser() {\n        var request = new CreateUserRequest(\"Alice\", \"alice@example.com\", 30);\n\n        var user = userService.createUser(request);\n\n        assertNotNull(user);\n        assertEquals(\"Alice\", user.getName());\n        assertEquals(\"alice@example.com\", user.getEmail());\n    }\n\n    @Test\n    @DisplayName(\"should throw exception when email is missing\")\n    void createUser_withoutEmail_throwsException() {\n        var request = new CreateUserRequest(\"Bob\", null, 25);\n\n        var exception = assertThrows(IllegalArgumentException.class,\n            () -> userService.createUser(request));\n\n        assertTrue(exception.getMessage().contains(\"email\"));\n    }\n\n    @Test\n    @DisplayName(\"should throw exception for duplicate email\")\n    void createUser_withDuplicateEmail_throwsException() {\n        var request = new CreateUserRequest(\"Alice\", \"alice@example.com\", 30);\n        userService.createUser(request);\n\n        assertThrows(DuplicateEmailException.class,\n            () -> userService.createUser(request));\n    }\n}\n```\n\n## Assertion Patterns\n\n```java\n@DisplayName(\"Assertion examples\")\nclass AssertionExamplesTest {\n\n    @Test\n    @DisplayName(\"equality assertions\")\n    void testEquality() {\n        assertEquals(4, 2 + 2);\n        assertNotEquals(5, 2 + 2);\n        assertEquals(0.3, 0.1 + 0.2, 0.001); \u002F\u002F delta for floating point\n    }\n\n    @Test\n    @DisplayName(\"boolean assertions\")\n    void testBooleans() {\n        assertTrue(10 > 5);\n        assertFalse(5 > 10);\n        assertNull(null);\n        assertNotNull(\"value\");\n    }\n\n    @Test\n    @DisplayName(\"grouped assertions with assertAll\")\n    void testGrouped() {\n        var user = new User(\"Alice\", \"alice@example.com\", 30);\n\n        assertAll(\"user properties\",\n            () -> assertEquals(\"Alice\", user.getName()),\n            () -> assertEquals(\"alice@example.com\", user.getEmail()),\n            () -> assertEquals(30, user.getAge())\n        );\n    }\n\n    @Test\n    @DisplayName(\"exception assertions\")\n    void testExceptions() {\n        var exception = assertThrows(ArithmeticException.class,\n            () -> { int result = 1 \u002F 0; });\n\n        assertEquals(\"\u002F by zero\", exception.getMessage());\n    }\n\n    @Test\n    @DisplayName(\"timeout assertions\")\n    void testTimeout() {\n        assertTimeout(Duration.ofSeconds(2), () -> {\n            Thread.sleep(100);\n        });\n    }\n\n    @Test\n    @DisplayName(\"iterable assertions\")\n    void testIterables() {\n        var list = List.of(1, 2, 3);\n        assertIterableEquals(List.of(1, 2, 3), list);\n    }\n}\n```\n\n## Parameterized Tests\n\n```java\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.*;\n\nclass ValidatorTest {\n\n    @ParameterizedTest\n    @ValueSource(strings = {\"user@example.com\", \"admin@test.org\", \"a@b.co\"})\n    @DisplayName(\"should accept valid emails\")\n    void isValidEmail_withValidEmails_returnsTrue(String email) {\n        assertTrue(Validators.isValidEmail(email));\n    }\n\n    @ParameterizedTest\n    @ValueSource(strings = {\"\", \"not-email\", \"@domain.com\", \"user@\"})\n    @DisplayName(\"should reject invalid emails\")\n    void isValidEmail_withInvalidEmails_returnsFalse(String email) {\n        assertFalse(Validators.isValidEmail(email));\n    }\n\n    @ParameterizedTest\n    @CsvSource({\n        \"1, 1, 2\",\n        \"0, 0, 0\",\n        \"-1, 1, 0\",\n        \"100, 200, 300\",\n        \"-50, -50, -100\"\n    })\n    @DisplayName(\"should add two numbers correctly\")\n    void add_withVariousInputs_returnsSum(int a, int b, int expected) {\n        assertEquals(expected, Calculator.add(a, b));\n    }\n\n    @ParameterizedTest\n    @MethodSource(\"provideAgeValidationData\")\n    @DisplayName(\"should validate age boundaries\")\n    void isValidAge_withBoundaryValues(int age, boolean expected) {\n        assertEquals(expected, Validators.isValidAge(age));\n    }\n\n    static Stream\u003CArguments> provideAgeValidationData() {\n        return Stream.of(\n            Arguments.of(0, false),\n            Arguments.of(1, true),\n            Arguments.of(17, false),\n            Arguments.of(18, true),\n            Arguments.of(120, true),\n            Arguments.of(121, false),\n            Arguments.of(-1, false)\n        );\n    }\n\n    @ParameterizedTest\n    @NullAndEmptySource\n    @ValueSource(strings = {\"  \", \"\\t\", \"\\n\"})\n    @DisplayName(\"should reject blank strings\")\n    void isBlank_withBlankStrings_returnsTrue(String input) {\n        assertTrue(Validators.isBlank(input));\n    }\n}\n```\n\n## Mockito Integration\n\n```java\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.*;\nimport static org.mockito.Mockito.*;\n\n@ExtendWith(MockitoExtension.class)\n@DisplayName(\"UserService with mocks\")\nclass UserServiceMockTest {\n\n    @Mock\n    private UserRepository userRepository;\n\n    @Mock\n    private EmailService emailService;\n\n    @InjectMocks\n    private UserService userService;\n\n    @Captor\n    private ArgumentCaptor\u003CUser> userCaptor;\n\n    @Test\n    @DisplayName(\"should save user to repository\")\n    void createUser_savesToRepository() {\n        var request = new CreateUserRequest(\"Alice\", \"alice@example.com\", 30);\n        when(userRepository.save(any(User.class)))\n            .thenAnswer(invocation -> {\n                var user = invocation.getArgument(0, User.class);\n                user.setId(1L);\n                return user;\n            });\n\n        userService.createUser(request);\n\n        verify(userRepository).save(userCaptor.capture());\n        var savedUser = userCaptor.getValue();\n        assertEquals(\"Alice\", savedUser.getName());\n        assertEquals(\"alice@example.com\", savedUser.getEmail());\n    }\n\n    @Test\n    @DisplayName(\"should send welcome email after creation\")\n    void createUser_sendsWelcomeEmail() {\n        when(userRepository.save(any())).thenAnswer(inv -> {\n            var user = inv.getArgument(0, User.class);\n            user.setId(1L);\n            return user;\n        });\n\n        userService.createUser(new CreateUserRequest(\"Bob\", \"bob@example.com\", 25));\n\n        verify(emailService).sendWelcomeEmail(\"bob@example.com\");\n        verifyNoMoreInteractions(emailService);\n    }\n\n    @Test\n    @DisplayName(\"should handle email failure gracefully\")\n    void createUser_emailFails_doesNotThrow() {\n        when(userRepository.save(any())).thenAnswer(inv -> {\n            var user = inv.getArgument(0, User.class);\n            user.setId(1L);\n            return user;\n        });\n        doThrow(new RuntimeException(\"SMTP error\"))\n            .when(emailService).sendWelcomeEmail(anyString());\n\n        assertDoesNotThrow(() ->\n            userService.createUser(new CreateUserRequest(\"Bob\", \"bob@example.com\", 25))\n        );\n    }\n}\n```\n\n## Nested Tests\n\n```java\n@DisplayName(\"ShoppingCart\")\nclass ShoppingCartTest {\n\n    private ShoppingCart cart;\n\n    @BeforeEach\n    void setUp() {\n        cart = new ShoppingCart();\n    }\n\n    @Nested\n    @DisplayName(\"when empty\")\n    class WhenEmpty {\n\n        @Test\n        @DisplayName(\"should have zero items\")\n        void hasZeroItems() {\n            assertEquals(0, cart.getItemCount());\n        }\n\n        @Test\n        @DisplayName(\"should have zero total\")\n        void hasZeroTotal() {\n            assertEquals(BigDecimal.ZERO, cart.getTotal());\n        }\n\n        @Test\n        @DisplayName(\"should throw when removing item\")\n        void throwsOnRemove() {\n            assertThrows(NoSuchElementException.class,\n                () -> cart.removeItem(\"Widget\"));\n        }\n    }\n\n    @Nested\n    @DisplayName(\"when items added\")\n    class WhenItemsAdded {\n\n        @BeforeEach\n        void addItems() {\n            cart.addItem(new CartItem(\"Widget\", new BigDecimal(\"9.99\"), 2));\n        }\n\n        @Test\n        @DisplayName(\"should update item count\")\n        void updatesItemCount() {\n            assertEquals(2, cart.getItemCount());\n        }\n\n        @Test\n        @DisplayName(\"should calculate total correctly\")\n        void calculatesTotal() {\n            assertEquals(new BigDecimal(\"19.98\"), cart.getTotal());\n        }\n\n        @Nested\n        @DisplayName(\"and discount applied\")\n        class AndDiscountApplied {\n\n            @Test\n            @DisplayName(\"should reduce total by discount percentage\")\n            void reducesTotal() {\n                cart.applyDiscount(0.1);\n                assertEquals(new BigDecimal(\"17.98\"), cart.getTotal());\n            }\n        }\n    }\n}\n```\n\n## Lifecycle Hooks\n\n```java\nclass LifecycleExampleTest {\n\n    @BeforeAll\n    static void setUpOnce() {\n        \u002F\u002F Runs once before all tests (must be static)\n        System.out.println(\"Setting up shared resources\");\n    }\n\n    @AfterAll\n    static void tearDownOnce() {\n        \u002F\u002F Runs once after all tests (must be static)\n        System.out.println(\"Cleaning up shared resources\");\n    }\n\n    @BeforeEach\n    void setUp() {\n        \u002F\u002F Runs before each test\n    }\n\n    @AfterEach\n    void tearDown() {\n        \u002F\u002F Runs after each test\n    }\n\n    @Test\n    void testExample() {\n        \u002F\u002F Test logic here\n    }\n}\n```\n\n## Best Practices\n\n1. **Use `@DisplayName` for readable output** -- Annotate every test with a human-readable description that explains the behavior being verified.\n2. **Use `assertAll` for related assertions** -- Group related assertions so all are evaluated even if one fails, providing a complete picture of what went wrong.\n3. **Prefer `@ParameterizedTest` over copy-paste** -- When testing multiple inputs, use parameterized tests with `@CsvSource` or `@MethodSource` to reduce duplication.\n4. **Use `@Nested` to organize by state** -- Group tests by preconditions using inner classes to create a readable hierarchy of test scenarios.\n5. **Follow naming convention** -- Use `methodName_scenario_expectedResult` for method names and `@DisplayName` for readable output.\n6. **Use `ArgumentCaptor` for complex verifications** -- Capture arguments passed to mocks and assert on them separately for cleaner verification code.\n7. **Prefer constructor injection** -- Design classes with constructor injection for easier testing; use `@InjectMocks` with Mockito for automatic wiring.\n8. **Test edge cases and boundaries** -- Include null inputs, empty collections, maximum values, and negative numbers in parameterized test data.\n9. **Use `assertThrows` over `@Test(expected=...)`** -- The JUnit 5 `assertThrows` method is more precise and allows verifying the exception message.\n10. **Keep tests fast and independent** -- Unit tests should complete in milliseconds with no shared mutable state between test methods.\n\n## Anti-Patterns\n\n1. **Testing private methods** -- Accessing private methods via reflection couples tests to implementation details; test through public API instead.\n2. **Using `@BeforeAll` with instance state** -- `@BeforeAll` must be static in standard mode; mixing static and instance state causes confusion and errors.\n3. **Ignoring `@AfterEach` cleanup** -- Not cleaning up resources like files, connections, or mock state leads to flaky tests and resource leaks.\n4. **Over-mocking** -- Mocking every dependency including simple value objects reduces test confidence; mock only external I\u002FO.\n5. **Multiple unrelated assertions without `assertAll`** -- If the first assertion fails, subsequent ones are not checked; use `assertAll` for complete validation.\n6. **Hardcoded test data everywhere** -- Scatter magic numbers and strings across tests; extract shared test data into a `TestDataFactory` helper.\n7. **Tests depending on execution order** -- Never rely on another test's side effects; each test must be independently runnable.\n8. **Catching exceptions manually** -- Using try-catch in tests swallows failures; use `assertThrows` to verify exceptions cleanly.\n9. **Not using `@ExtendWith(MockitoExtension.class)`** -- Manually initializing mocks with `MockitoAnnotations.openMocks()` is error-prone; use the extension.\n10. **Ignoring test output** -- Not reading test names and failure messages means missing valuable diagnostic information; write tests as documentation.\n",{"name":5,"tags":24,"agents":33,"author":44,"domains":45,"license":15,"version":49,"languages":50,"frameworks":51,"description":7,"testingTypes":52},[25,26,27,28,29,30,31,32],"junit5","java","unit-testing","mockito","parameterized","assertions","tdd","integration",[34,35,36,37,38,39,40,41,42,43],"claude-code","cursor","github-copilot","windsurf","codex","aider","continue","cline","zed","bolt","thetestingacademy",[46,47,48],"web","api","backend","1.0.0",[26],[25],[53,32],"unit","main","eac438ea9216a7cb8a570cab803eb01eeafc6793","skills\u002Fqa\u002Fjunit5-testing","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m","https:\u002F\u002Fid-preview--d5bef06d-6c9e-4563-a3d4-a855b9d61610.lovable.app\u002F","Eu sou Antonio B. B. Ndombe \r\nEu sou um Pesquisador e desenvolvedor de sistema autonomia cognitivos, fundador de sistemas ODCs ( organismos digital conscientes)","2026-11-04 06:53:39",{"source":8,"skillName":5,"commands":63,"prompt":73,"zipAvailable":74},[64,67,70],{"tool":65,"command":66},"npx","npx skills add GrupoANDevelopment-m\u002Falberto-ai --skill \"JUnit 5 Testing\"",{"tool":68,"command":69},"bunx","bunx skills add GrupoANDevelopment-m\u002Falberto-ai --skill \"JUnit 5 Testing\"",{"tool":71,"command":72},"pnpm","pnpm dlx skills add GrupoANDevelopment-m\u002Falberto-ai --skill \"JUnit 5 Testing\"","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fskills\u002Fqa\u002Fjunit5-testing\u002FSKILL.md",true,[],[77,85,92,99,106,113],{"skillNo":78,"name":79,"nameZh":6,"description":80,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":81,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":84,"publishedAt":19},"01M7DASC6FSCJP49JZBSK0NY1Z","nemoclaw-user-reference","Describes the NemoClaw integration layer and blueprint architecture and how they orchestrate compatible agent sandboxes. Use when looking up architecture, agent integration, plugin structure, or blueprint design. Trigger keywords - nemoclaw architecture, nemoclaw agent architecture, nemoclaw plugin blueprint structure, nemoclaw vs openshell, which cli, nemoclaw cli, openshell cli, sandbox commands, nemoclaw cli commands, nemoclaw command reference, nemoclaw network policy, sandbox egress control operator approval, nemoclaw troubleshooting, nemoclaw debug sandbox issues.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-reference\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":81,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":91,"publishedAt":19},"01M7DAS5S79VK33XFGVAM2TFWF","nemoclaw-user-overview","Explains how OpenClaw, OpenShell, and NemoClaw form the ecosystem, NemoClaw's position in the stack, what NemoClaw adds beyond the community sandbox, and when to prefer NemoClaw versus integrating OpenShell and OpenClaw directly. Use when users ask about the relationship between OpenClaw, OpenShell, and NemoClaw, or when to use NemoClaw versus OpenShell. Trigger keywords - nemoclaw ecosystem, openclaw openshell, nemoclaw vs openshell, sandboxed openclaw, how nemoclaw works, nemoclaw sandbox lifecycle blueprint, nemoclaw overview, openclaw always-on assistants, nvidia openshell, nvidia nemotron, nemoclaw release notes, nemoclaw changelog.","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-overview\u002FSKILL.md",[],[],{"skillNo":93,"name":94,"nameZh":6,"description":95,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":81,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":98,"publishedAt":19},"01M7DAS0PQEMET5MNGZ1ABSZVS","nemoclaw-user-monitor-sandbox","Inspects sandbox health, traces agent behavior, and diagnoses problems. Use when monitoring a running sandbox, debugging agent issues, or checking sandbox logs. Trigger keywords - monitor nemoclaw sandbox, debug nemoclaw agent issues.","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-monitor-sandbox\u002FSKILL.md",[],[],{"skillNo":100,"name":101,"nameZh":6,"description":102,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":81,"licenseFileUrl":6,"sourceHtmlUrl":103,"categories":104,"keywords":105,"publishedAt":19},"01M7DARQYGF7E9A69VH6S3CGN9","nemoclaw-user-manage-sandboxes","Explains operational tasks after the quickstart: listing sandboxes, status and health checks, logs, diagnostics, port forwards, multiple sandboxes, credential reset, rebuilds, network presets, upgrades, and uninstall. Trigger keywords - manage nemoclaw sandboxes, nemoclaw status, nemoclaw list, nemoclaw dashboard port, nemoclaw rebuild, nemoclaw upgrade sandboxes, nemoclaw uninstall, sandbox mutability, sandbox runtime configuration, sandbox rebuild, nemoclaw backup, nemoclaw restore, workspace backup, openshell sandbox download upload, nemoclaw messaging channels, nemoclaw telegram, nemoclaw discord, nemoclaw slack, nemoclaw wechat, nemoclaw whatsapp, openshell channel messaging, nemoclaw workspace files, soul.md, user.md, identity.md, agents.md, sandbox persistence.","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-manage-sandboxes\u002FSKILL.md",[],[],{"skillNo":107,"name":108,"nameZh":6,"description":109,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":81,"licenseFileUrl":6,"sourceHtmlUrl":110,"categories":111,"keywords":112,"publishedAt":19},"01M7DARH8BRTG51TRCAB7YWDJ4","nemoclaw-user-manage-policy","Adds, removes, or modifies allowed endpoints in the sandbox policy. Use when customizing network policy, changing egress rules, or configuring sandbox endpoint access. Trigger keywords - customize nemoclaw network policy, sandbox egress policy configuration, nemoclaw integration policy examples, post-install policy setup, openshell approval workflow, policy preset, nemoclaw approve network requests, sandbox egress approval tui.","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-manage-policy\u002FSKILL.md",[],[],{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":81,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":119,"publishedAt":19},"01M7DARAT62M2VR37590VCZ5NS","nemoclaw-user-get-started","Installs NemoClaw, launches a sandbox, and runs the first agent prompt. Use when onboarding, installing, or launching a NemoClaw sandbox for the first time. Trigger keywords - nemoclaw quickstart, install nemoclaw openclaw sandbox, nemohermes quickstart, hermes agent nemoclaw, run hermes openshell sandbox, nemoclaw prerequisites, nemoclaw supported platforms, nemoclaw hardware software, nemoclaw windows wsl2 setup, nemoclaw install windows docker desktop.","https:\u002F\u002Fgithub.com\u002FGrupoANDevelopment-m\u002Falberto-ai\u002Fblob\u002Fmain\u002Fupstream\u002Fnemoclaw\u002Fskills\u002Fnemoclaw-user-get-started\u002FSKILL.md",[],[]]