---
description: Java Testing
alwaysApply: false
---

# Java Testing

Test behavior, not implementation. JUnit 5 + AssertJ + Mockito + Testcontainers.

## Unit Test

```java
class OrderServiceTest {
    private final OrderRepository repo = mock(OrderRepository.class);
    private final OrderService sut = new OrderService(repo);
    @Test void create_valid_savesOrder() {
        var result = sut.create(new CreateOrderRequest("c1", List.of(item)));
        assertThat(result.status()).isEqualTo(OrderStatus.PENDING);
    }
}
```

## Naming: `method_scenario_expectedBehavior`
```java
@Test void findById_whenNotFound_returnsEmpty() {}
@Test void create_duplicateEmail_throwsConflict() {}
```

## Parameterized Tests

```java
@ParameterizedTest @CsvSource({"'', false", "ab, false", "abc, true"})
void validatePassword(String input, boolean expected) {
    assertThat(PasswordValidator.isValid(input)).isEqualTo(expected);
}
```

## Integration Tests (Testcontainers)

```java
@SpringBootTest(webEnvironment = RANDOM_PORT) @Testcontainers
class OrderControllerIT {
    @Container static PostgreSQLContainer<?> pg = new PostgreSQLContainer<>("postgres:16-alpine");
    @DynamicPropertySource static void props(DynamicPropertyRegistry r) {
        r.add("spring.datasource.url", pg::getJdbcUrl);
    }
    @Autowired TestRestTemplate rest;
    @Test void post_returns201() {
        var resp = rest.postForEntity("/api/v1/orders", req, OrderResponse.class);
        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.CREATED);
    }
}
```

## WireMock

```java
@WireMockTest(httpPort = 8089)
class PaymentTest {
    @Test void charge_success() {
        stubFor(post("/charge").willReturn(okJson("""{"txId":"tx-1"}""")));
        assertThat(svc.charge(req).txId()).isEqualTo("tx-1");
    }
}
```

## Architecture Tests (ArchUnit)

```java
noClasses().that().resideInAPackage("..domain..")
    .should().dependOnClassesThat().resideInAnyPackage("..infrastructure..", "..api..")
    .check(classes);
```

## Anti-Patterns

- `@SpringBootTest` for unit tests — use plain JUnit + Mockito instead
- `Thread.sleep` for waits — use Awaitility
- Shared mutable state — reset in `@BeforeEach`
