# BDD Style and Mocking Guidelines — JUnit 5 & Mockito

Source: https://www.skillbyai.com/en/java-testing/v-bdd

> Write readable given-when-then tests with BDDMockito and avoid common mocking mistakes.

## Readable tests and sensible mocking

**BDDMockito** offers aliases that read like behaviour specifications: `given(catalog.priceOf("pen")).willReturn(price)` for stubbing and `then(mailer).should().send(any())` for verification, matching the given-when-then structure of a test. Beyond style, a few guidelines keep mock-based tests valuable. **Do not mock what you do not own**: mocking third-party APIs (HTTP clients, AWS SDKs) bakes your assumptions about them into tests; wrap them in your own small interface (an adapter) and test the adapter with integration tests. **Do not mock value objects** such as `Money` or `LocalDate`; create real ones. **Do not mock the class under test**. **Avoid deep stubs** (`RETURNS_DEEP_STUBS` chains) that mirror long call chains, a design smell. **Prefer few mocks per test**: needing many mocks often means the class has too many responsibilities. And remember that mocks verify that code **calls** collaborators as you expect, not that those collaborators work; integration tests are still needed.

## Given-when-then with BDDMockito

The test reads like a specification of the behaviour.

```java
import static org.mockito.BDDMockito.*;

@ExtendWith(MockitoExtension.class)
class LoyaltyServiceTest {

    @Mock PointsLedger ledger;
    @Mock Notifier notifier;

    @Test
    void goldMembersEarnDoublePointsAndAreNotified() {
        // given
        given(ledger.tierOf("c-42")).willReturn(Tier.GOLD);
        var service = new LoyaltyService(ledger, notifier);

        // when
        service.recordPurchase("c-42", Money.inr("1000.00"));

        // then
        then(ledger).should().addPoints("c-42", 200);          // 2 x 100 points
        then(notifier).should().pointsEarned("c-42", 200);
        then(notifier).shouldHaveNoMoreInteractions();
    }
}

// adapter around a third-party SDK: mock YOUR interface, integration-test the adapter
interface SmsSender { void send(String phone, String text); }
final class TwilioSmsSender implements SmsSender { /* calls the vendor SDK */ }
```

## Rehearsing with stand-ins

Stand-ins are fine for rehearsing an actor's own scene, but you would never judge the whole play on a rehearsal where every other actor is a stand-in. Mock-heavy unit tests need real integration tests alongside them.

**Quiz:** According to common mocking guidelines, how should you test code that uses a third-party SDK?

- [ ] Mock the SDK classes directly everywhere
- [ ] Never test it
- [x] Wrap the SDK in your own interface, mock that interface in unit tests, and integration-test the adapter
- [ ] Use static mocks for every SDK call

*Answer:* Wrap the SDK in your own interface, mock that interface in unit tests, and integration-test the adapter. Owning the interface keeps unit tests stable and isolates SDK assumptions in a tested adapter.
