Use when writing tests for Java (Quarkus or Spring) code - JUnit Jupiter structure, Mockito for collaborators, test slices, Testcontainers, and framework-native integration tests, test-first
Installs into .claude/skills of the current project.
Are you the author of Java Testing Junit Mockito?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/makifbaysal-java-testing-junit-mockito)
---
name: java-testing-junit-mockito
category: testing
description: Use when writing tests for Java (Quarkus or Spring) code - JUnit Jupiter structure, Mockito for collaborators, test slices, Testcontainers, and framework-native integration tests, test-first
tech_stack: Java
source: sivaprasadreddy/sivalabs-agent-skills (MIT), adapted
---
# Java Testing (JUnit Jupiter + Mockito)
## Overview
Test-first in Java, same discipline as everywhere (see tdd-workflow): write the failing test, watch it fail, minimal code to pass. This skill is the Java mechanics for that cycle.
**Core principle:** Unit-test the domain and services as plain Java without booting the framework. Reserve framework-integration tests for the boundary (HTTP, persistence).
## Test levels
| Level | What it covers | Tools |
|-------|-----------------|-------|
| Unit | Domain entity / value object, no mocks | JUnit Jupiter |
| Unit with mocks | Service (mock its ports) | JUnit Jupiter + Mockito |
| Persistence slice | Repository against a real DB, no HTTP layer | `@DataJpaTest` (Spring) + Testcontainers `@ServiceConnection`; Quarkus `@QuarkusTest` with Dev Services |
| Web slice | Controller/resource wiring, serialization, validation — mocked service | `@WebMvcTest` (Spring); Quarkus `@QuarkusTest` + `@InjectMock` |
| End-to-end | Full app boot, real DB, real HTTP | `@SpringBootTest(webEnvironment=RANDOM_PORT)` + `RestTestClient` (Spring Boot 4); `@QuarkusTest` + RestAssured |
Most tests are the first two — fast, no container. Boot the framework only where you're testing wiring, serialization, or SQL; reach for the narrowest slice that proves it before a full `@SpringBootTest`/plain `@QuarkusTest`.
## JUnit Jupiter + Mockito unit test
```java
class TaskServiceTest {
private final TaskRepository repo = mock(TaskRepository.class);
private final TaskService service = new TaskService(repo); // constructor injection pays off
@Test
void create_persists_and_returns_task() {
Task t = service.create("Write the report");
verify(repo).persist(any(Task.class)); // or save(...) for Spring
assertEquals("Write the report", t.title());
}
@Test
void create_rejects_blank_title() {
assertThrows(InvalidTaskTitle.class, () -> service.create(" "));
verifyNoInteractions(repo); // invalid input never hits the DB
}
}
```
## Rules
- **Mock collaborators (ports), never the class under test.** A test whose asserts only exercise the mock proves nothing — assert on the real object's behavior/return.
- One behavior per `@Test`, named for the behavior (`create_rejects_blank_title`).
- Prefer real value objects over mocking them — they're cheap to construct.
- `@ParameterizedTest` for boundary tables (200 chars ok, 201 rejected).
- Integration tests use Testcontainers for a real Postgres, not H2 — test against the DB you deploy on. Seed per test with the repository's real migrations plus minimal inserts; never share state between tests.
- AssertJ (`assertThat(...)`) is fine and reads well; match the repo's existing assertion style.
- **Spring Boot 4:** `@MockitoBean`/`@MockitoSpyBean` replace `@MockBean`/`@SpyBean`, which were removed in Boot 4.0 — use the old annotations only on a repo still pinned to Boot 3.x.
- **Quarkus:** `@InjectMock` inside a `@QuarkusTest` for a CDI-bean mock; RestAssured for the HTTP-level assertion: `given().when().post("/tasks").then().statusCode(201)`.
- **Testcontainers Java 2.x** coordinates: artifacts are prefixed `testcontainers-*` (e.g. `org.testcontainers:testcontainers-postgresql`), class `org.testcontainers.postgresql.PostgreSQLContainer`; JUnit 4 support was removed, Jupiter only.
- On JDK 21+, register `mockito-core` as a `-javaagent` in the build's surefire/failsafe config if the repo doesn't already, to avoid the dynamic-agent-loading warning.
## Common Mistakes
- `@SpringBootTest`/plain `@QuarkusTest` on everything — slow, and hides design problems that plain unit tests would surface.
- Mocking the service you're testing.
- Asserting `verify(mock)` only, never checking the returned/observable value.
- H2 in tests but Postgres in prod — dialect gaps ship bugs.
- Using `@MockBean` on a Spring Boot 4 repo (removed; use `@MockitoBean`).
## Red Flags
- The test passes without the production code being written (it tests the mock).
- A unit test that needs a running database.
- No failing-first run — you wrote the test after the code.