agentsclimarketplace

Java testing

Skill mtkhawaja/java-skills/skills/java-testing

Use when writing or reviewing Java tests, unit or integration — JUnit Jupiter with AssertJ assertions, the @DisplayName / method-name mirroring convention, behavior-focused naming, Mockito (annotation mocks, no deep stubs, @MockitoBean for Spring), and Testcontainers with @ServiceConnection for real backing services (database, Kafka, Redis). Catches JUnit-assertion use, class-level @DisplayName, imperative or deep-stub mocking, H2-instead-of-real-DB, container-per-method, and slow/flaky suites.From its SKILL.md

Install
npx -y skills add mtkhawaja/java-skills --skill java-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

  • 0 stars0 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.5 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it

Java Testing

Overview

Tests document behavior. The house stack: JUnit Jupiter for structure, AssertJ for assertions, Mockito (annotation-driven) for isolation, and Testcontainers + @ServiceConnection when a test needs a real backing service. For container lifecycle and suite-wide sharing details see references/testcontainers-patterns.md.

Structure & assertions

  • JUnit Jupiter (5+) by default, unless the repo standardizes on something else.
  • AssertJ for assertionsassertThat(x).isEqualTo(y) / assertThatThrownBy(...), never JUnit's assertEquals/assertTrue.
  • Every test has a method-level @DisplayName in natural language. Never at the class level (including @Nested).
  • The method name mirrors the @DisplayName in camelCase and describes behavior, not implementation: shouldDoXWhenY, shouldNotDoXWhenYIsCondition. Never a test prefix.
  • One behavior per test; order-independent and parallel-safe (no shared mutable state).
  • Prefer @ParameterizedTest for input matrices and edge-case grids over duplicated bodies.
  • Never skip/disable/comment out a test to make the build pass.
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

class NotificationServiceTest {
    @DisplayName("Should save published event When the producer publishes a new event")
    @Test
    void shouldSavePublishedEventWhenTheProducerPublishesANewEvent() {
        assertThat(service.publish(event).status()).isEqualTo(Status.SAVED);
    }
}

Mockito

  • Use the JUnit Jupiter integration: @ExtendWith(MockitoExtension.class). Don't call MockitoAnnotations.openMocks(...) or create mocks imperatively with mock(...).
  • Prefer annotation mocks: @Mock, @Spy, @Captor, @InjectMocks.
  • Avoid brittle setups — no deep stubbing or mock-heavy tests tied to implementation details; prefer fakes or real collaborators. Stub only inside the test that needs it (no global stubbing).
  • For Spring-managed beans use @MockitoBean (prefer over the older @MockBean).
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock OrderRepository repository;
    @InjectMocks OrderService service;
}

@SpringBootTest
class PaymentControllerTest {
    @MockitoBean PaymentService paymentService;   // prefer over @MockBean
}

Integration tests (Testcontainers)

Integration tests exercise the real backing service against production parity, fast and isolated:

  • Use a real engine via Testcontainers, not H2/in-memory. H2's compatibility mode hides dialect/type/identity/RETURNING differences — doubly true for Spring Data JDBC, which emits SQL almost literally. Match the prod engine and major version (postgres:16-alpine).
  • Wire it with @ServiceConnection (Spring Boot 3.1+, needs the spring-boot-testcontainers test dependency). It auto-creates the *ConnectionDetails bean — prefer it over manual @DynamicPropertySource, which is the older, more verbose fallback.
  • Container fields are static. A non-static @Container starts a fresh container per test method — slow. Static = once per class.
  • Share containers across the whole suite, not per class — a shared singleton (or a Spring-managed container in an imported @TestConfiguration base) so the suite starts one Postgres, not one per test class. See the reference.
  • Isolate state between tests. Don't let one test's data leak into another. Use @Transactional rollback (repository slice tests) or reset (truncate / Flyway clean) in setup; never rely on order.
  • Use the narrowest slice that exercises the behavior: @DataJdbcTest (JDBC beans only, rolls back per test) for repository tests; @SpringBootTest only when you need the full service path.
  • Separate integration tests from unit tests so a slow container suite doesn't run on every mvn test: name them *IT (Maven Failsafe) vs *Test (Surefire), and/or @Tag("integration").
  • Don't hand-manage start()/stop() for @Container fields (the extension owns the lifecycle) — only for the deliberate singleton/reuse patterns in the reference.
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
// ... plus the usual JUnit/AssertJ/Spring imports

@Testcontainers
@SpringBootTest
class OrderPersistenceIT {

    @Container
    @ServiceConnection                                 // auto-wires spring.datasource.* — no @DynamicPropertySource
    static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>("postgres:16-alpine");

    @Autowired OrderService orderService;
    @Autowired OrderRepository orderRepository;

    @DisplayName("Should persist order and read it back When place is called")
    @Test
    void shouldPersistOrderAndReadItBackWhenPlaceIsCalled() {
        var placed = orderService.place(new Order(null, "customer-42", BigDecimal.valueOf(99.95)));
        assertThat(orderRepository.findById(placed.id())).get()
            .satisfies(found -> assertThat(found.customerId()).isEqualTo("customer-42"));
    }
}

Common mistakes

RationalizationReality
"assertEquals is fine / imported"House standard is AssertJ — reads as behavior, better failures.
"@DisplayName on the @Nested class groups nicely"Class-level @DisplayName is forbidden; put it on each method.
"testGet is clear enough"Behavior names: shouldReturnAccountWhenIdExists. No test prefix.
"openMocks in @BeforeEach"Use @ExtendWith(MockitoExtension.class) + @Mock.
"Deep stubs keep the test short"Brittle — restructure or use a fake/real collaborator.
"I'll @Disabled this flaky one"Never disable tests to go green; fix the test or the code.
"H2 in-memory is faster for tests"It hides real SQL/dialect bugs — Spring Data JDBC especially. Use the prod engine.
"@DynamicPropertySource works fine"@ServiceConnection is the modern, terse replacement (Boot 3.1+).
"Each test/class gets its own container"Non-static @Container = container per method; per class is still slow at scale. static, shared across the suite.
"Integration tests run with the unit tests"Separate *IT/Failsafe from *Test/Surefire so mvn test stays fast.

Red flags — stop

  • org.junit.jupiter.api.Assertions / assertEquals imported
  • @DisplayName on a class or @Nested class; a test-prefixed name; name ≠ its @DisplayName
  • MockitoAnnotations.openMocks, imperative mock(...), deep stubs, @MockBean when @MockitoBean exists
  • Tests sharing static/mutable state, or depending on execution order / leftover data
  • H2/hsqldb/:mem: standing in for the production database
  • A non-static @Container; manual @DynamicPropertySource where @ServiceConnection applies
  • A @SpringBootTest where a @DataJdbcTest/@WebMvcTest slice would do

What ships with it: 1 file

4.4 KB alongside SKILL.md

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.