# Case Study: Testing an Order Service End to End — JUnit 5 & Mockito

Source: https://www.skillbyai.com/en/java-testing/p-case

> Combine unit, slice and integration tests into a balanced suite.

## A balanced test suite

Consider an `OrderService` that prices a cart, applies coupons, charges a payment gateway, saves the order and sends a confirmation email. A balanced suite looks like this. **Pure unit tests** (plain JUnit, no mocks) cover the pricing and coupon rules with parameterised boundary cases: they are numerous, fast and stable. **Service unit tests** with Mockito cover orchestration: a declined payment saves nothing and sends no email; a successful payment saves a PAID order and sends exactly one email (verified with a captor); a broker failure after payment is handled correctly. Collaborators are interfaces (`PaymentGateway`, `OrderRepository`, `Mailer`) and time comes from an injected `Clock`. **Slice tests** check the REST controller's status codes, validation and JSON with `@WebMvcTest` and a `@MockitoBean` service. **Repository tests** check custom queries against real PostgreSQL with `@DataJpaTest` and Testcontainers. A few **integration tests** run the whole checkout with a real database and a WireMock payment provider. CI runs everything on each pull request, reports coverage, and runs PIT nightly on the pricing module.

## The suite at a glance

Each layer tests what it is best at.

```text
layer                     tool                              count   speed      what it proves
------------------------  --------------------------------  ------  ---------  ------------------------------------
pricing & coupon rules    JUnit + @ParameterizedTest        ~60     ms         business rules and boundaries
OrderService orchestration JUnit + Mockito (+ captors)       ~15     ms         calls, side effects, error handling
OrderController           @WebMvcTest + MockMvc             ~10     < 1 s      HTTP status, validation, JSON shape
OrderRepository queries   @DataJpaTest + Testcontainers     ~8      seconds    SQL, mappings, constraints
checkout flow             @SpringBootTest + Testcontainers  ~3      seconds    wiring, transactions, real DB
                          + WireMock payment provider
nightly                   PIT on pricing module             -       minutes    assertions catch behaviour changes
```

## Push tests down the pyramid

When an integration test catches a bug in a pricing rule, also add a fast unit test for that rule. The next regression is then caught in milliseconds at the right level.

**Quiz:** In the case study, how is the rule "a declined payment must not send an email" best tested?

- [x] A service unit test with mocks that verifies the mailer is never called
- [ ] A full end-to-end UI test
- [ ] A JaCoCo report
- [ ] A database migration

*Answer:* A service unit test with mocks that verifies the mailer is never called. Interaction-focused behaviour of the service is cheapest to check with Mockito and verify(never()).
