agentsclimarketplace

Junit testing

Skill VersoXBT/claude-initial-setup/skills/java/junit-testing

JUnit 5 testing patterns including annotations, Mockito mocking, Spring Boot test slices, MockMvc, Testcontainers integration, and parameterized tests. Use when the user is writing Java tests, setting up test infrastructure, mocking dependencies, testing Spring controllers, or running integration tests with real databases. Trigger on any mention of JUnit, Mockito, @SpringBootTest, MockMvc, Testcontainers, or Java testing.From its SKILL.md

Install
npx -y skills add VersoXBT/claude-initial-setup --skill junit-testing

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

8.2 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it

JUnit 5 Testing Patterns

Patterns for writing effective unit, integration, and slice tests in Java.

When to Use

  • User is writing JUnit 5 tests
  • User needs to mock dependencies with Mockito
  • User asks about Spring Boot test slices (@WebMvcTest, @DataJpaTest)
  • User wants integration tests with real databases (Testcontainers)
  • User needs parameterized tests for multiple inputs

Core Patterns

JUnit 5 Annotations and Lifecycle

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

class UserServiceTest {
    private UserService userService;
    private UserRepository userRepository;

    @BeforeEach
    void setUp() {
        userRepository = new InMemoryUserRepository();
        userService = new UserService(userRepository);
    }

    @Test
    @DisplayName("creates a user with valid input")
    void createsUserWithValidInput() {
        User result = userService.createUser(new CreateUserRequest("Alice", "[email protected]"));
        assertAll(
            () -> assertNotNull(result.getId()),
            () -> assertEquals("Alice", result.getName()),
            () -> assertEquals("[email protected]", result.getEmail())
        );
    }

    @Test
    @DisplayName("throws when email is already taken")
    void throwsWhenEmailTaken() {
        userRepository.save(new User("Bob", "[email protected]"));
        assertThrows(ConflictException.class,
            () -> userService.createUser(new CreateUserRequest("Bob2", "[email protected]")));
    }

    @Nested
    @DisplayName("when user exists")
    class WhenUserExists {
        private User existingUser;

        @BeforeEach
        void setUp() {
            existingUser = userRepository.save(new User("Alice", "[email protected]"));
        }

        @Test
        void findsById() {
            Optional<User> found = userService.findById(existingUser.getId());
            assertTrue(found.isPresent());
            assertEquals("Alice", found.get().getName());
        }
    }
}

Mockito -- Mocking Dependencies

Isolate the unit under test by mocking its collaborators.

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock private OrderRepository orderRepository;
    @Mock private PaymentGateway paymentGateway;
    @InjectMocks private OrderService orderService;

    @Test
    @DisplayName("places order and charges payment")
    void placesOrderAndChargesPayment() {
        var order = new Order("product-1", 2, 29_99L);
        when(orderRepository.save(any(Order.class))).thenReturn(order);
        when(paymentGateway.charge(anyLong())).thenReturn(new PaymentResult(true, "txn-123"));

        assertNotNull(orderService.placeOrder(order));
        verify(paymentGateway).charge(29_99L);
        verify(orderRepository).save(order);
    }

    @Test
    @DisplayName("rolls back order when payment fails")
    void rollsBackWhenPaymentFails() {
        when(paymentGateway.charge(anyLong())).thenReturn(new PaymentResult(false, null));
        assertThrows(PaymentFailedException.class,
            () -> orderService.placeOrder(new Order("product-1", 1, 50_00L)));
        verify(orderRepository, never()).save(any());
    }
}

@WebMvcTest -- Controller Slice Tests

Test controllers in isolation without starting the full application context. Only the web layer is loaded.

@WebMvcTest(UserController.class)
class UserControllerTest {
    @Autowired private MockMvc mockMvc;
    @MockBean private UserService userService;

    @Test
    @DisplayName("GET /api/v1/users/{id} returns user")
    void getUser() throws Exception {
        when(userService.findById("1"))
            .thenReturn(Optional.of(new UserResponse("1", "Alice", "[email protected]")));

        mockMvc.perform(get("/api/v1/users/1"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.data.name").value("Alice"));
    }

    @Test
    @DisplayName("POST /api/v1/users validates request body")
    void validateCreateUser() throws Exception {
        mockMvc.perform(post("/api/v1/users")
                .contentType("application/json")
                .content("{\"name\":\"\",\"email\":\"not-an-email\"}"))
            .andExpect(status().isBadRequest());
    }
}

Testcontainers -- Integration Tests with Real Databases

Spin up real database containers for integration tests. Tests are reliable and match production behavior.

@SpringBootTest
@Testcontainers
class UserRepositoryIntegrationTest {
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")
        .withDatabaseName("testdb").withUsername("test").withPassword("test");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @Autowired private UserRepository userRepository;

    @Test
    @DisplayName("persists and retrieves a user")
    void persistsAndRetrieves() {
        User saved = userRepository.save(new User("Alice", "[email protected]"));
        Optional<User> found = userRepository.findById(saved.getId());
        assertTrue(found.isPresent());
        assertEquals("Alice", found.get().getName());
    }
}

Parameterized Tests

Run the same test logic with different inputs. Reduces duplication.

class EmailValidatorTest {
    private final EmailValidator validator = new EmailValidator();

    @ParameterizedTest
    @CsvSource({"[email protected], true", "[email protected], true", "invalid, false", "@no-local.com, false"})
    void validatesEmail(String email, boolean expected) {
        assertEquals(expected, validator.isValid(email));
    }

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

    @ParameterizedTest
    @MethodSource("validEmailProvider")
    void acceptsValidEmails(String email) { assertTrue(validator.isValid(email)); }

    static Stream<String> validEmailProvider() {
        return Stream.of("[email protected]", "[email protected]", "[email protected]");
    }
}

Anti-Patterns

  • Testing implementation details -- Verifying private method calls or internal state. Test behavior (inputs and outputs), not how the code achieves the result.
  • Overusing @SpringBootTest -- Loads the entire application context. Slow. Use slice annotations (@WebMvcTest, @DataJpaTest) to load only what you need.
  • Not using @ExtendWith(MockitoExtension.class) -- Forgetting this means @Mock and @InjectMocks annotations are silently ignored, leading to NullPointerException.
  • Sharing mutable state between tests -- Tests must be independent. Use @BeforeEach to reset state. Never rely on test execution order.
  • Mocking everything including the class under test -- Only mock collaborators. The class under test should use its real implementation.

Quick Reference

CategoryKey APIs
Lifecycle@BeforeEach, @AfterEach, @BeforeAll, @AfterAll, @Nested
AssertionsassertEquals, assertTrue, assertThrows, assertAll, assertTimeout
Mockitowhen().thenReturn(), verify(), any(), @Mock, @InjectMocks
Slices@WebMvcTest (controllers), @DataJpaTest (JPA), @SpringBootTest (full)
Parameterized@CsvSource, @ValueSource, @MethodSource, @NullAndEmptySource

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Gives 2 of the 12 instructions most unit integration skills give in ~1.7k tokens

Counted across 149 of the 202 authors here whose files we hold, read 2026-09-06

  • Structure tests as arrange, act, assertin 17 of 149
  • Test behavior, not implementationhere, and in 16 of 149
  • Mock all external dependenciesin 14 of 149
  • Keep tests independenthere, and in 9 of 149
  • Implement minimal code to passin 7 of 149
  • Write a failing test firstin 7 of 149
  • Fix or delete flaky tests immediatelyin 7 of 149
  • Verify one behaviour per test functionin 7 of 149
  • Name tests MethodName_StateUnderTest_ExpectedBehaviorin 6 of 149
  • Separate arrange, act, and assert with blank linesin 6 of 149
  • Target at least 80 percent core logic coveragein 6 of 149
  • Create factory fixtures for test datain 6 of 149

Said here and by no other author read

  • Mock collaborators to isolate the unit under test
  • Apply MockitoExtension for @Mock and @InjectMocks
  • Test controllers in isolation with @WebMvcTest
  • Prefer slice annotations over full @SpringBootTest
  • Wire container properties with @DynamicPropertySource

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.