Lesson 15 / 25
BDD Style and Mocking Guidelines
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.
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.
Quick check: 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
- 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.