Back to SDET Scenarios
Q9Question 9 of 12

What is mocking and stubbing, and the tools used

Key Concepts
  • A stub returns preset values to control what the code under test sees, used for state-based assertions
  • A mock records how it was called so you can verify interactions: method, arguments, call count, order
  • Mockito creates test doubles for Java objects: mock, stub methods, inject, verify interactions
  • WireMock creates a fake HTTP server for external REST APIs with configurable responses, delays, and faults
  • Mock at the right boundary: mock external or slow collaborators, not the class under test
  • Prefer stubs for state-based assertions and mocks only when the interaction itself is the behavior to verify

Definition

A stub returns preset values to control what the code sees, a mock also records calls so you can verify interactions, and you create them with Mockito for Java objects and WireMock for HTTP services.
  • Stub – A fake that returns preset values to control what the code under test sees. Used for state-based assertions.
  • Mock – A fake that records how it was called so you can verify interactions: method, arguments, call count.
  • Mockito – Standard Java library for creating test doubles of classes and interfaces.
  • WireMock – Fake HTTP server for mocking external REST APIs with configurable responses, delays, and faults.
  • Other doubles – Dummies (passed but unused), fakes (lightweight implementations), spies (wrap real objects).
  • Right boundary – Mock external or slow collaborators, not the class under test. Do not over-mock.

When testing the National Bank of India's transfer logic in isolation, you do not want to hit the real fraud-check service or the real email sender, because they are slow, external, and unpredictable. So you use fakes. A stub is a stand-in that simply gives a canned answer, for example a fake fraud service that always says "approved," so you can control what the transfer logic sees. A mock is a stand-in that also keeps a log of how it was used, for example a fake notification service that records "you called me once with Rajesh's email," so you can verify that the transfer logic actually tried to notify the customer. Tools like Mockito create these fakes for Java objects, and WireMock creates a fake HTTP server for external web services.

Understanding the Concepts

What is a stub?

A stub is a fake that returns preset values to control what the code under test sees. You use it when you care about the result, not the interaction.

JAVA
// Stub: control what the code sees
package com.bank.service;

import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class CurrencyConverterTest {

    @Test
    void convertsUsingStubRate() {
        // STUB: return a fixed rate regardless of how many times called
        ExchangeRateService rateService = mock(ExchangeRateService.class);
        when(rateService.getRate("USD", "INR")).thenReturn(new BigDecimal("83.50"));

        CurrencyConverter converter = new CurrencyConverter(rateService);
        BigDecimal result = converter.convert(
            new BigDecimal("1000.00"), "USD", "INR"
        );

        // Assert the RESULT (state-based)
        assertEquals(new BigDecimal("83500.00"), result);
        // We do NOT verify how many times getRate was called
    }
}

What is a mock?

A mock records how it was called so you can verify interactions. You use it when the correct behavior IS the interaction.

JAVA
// Mock: verify interactions
@Test
void transferNotifiesCustomer() {
    // MOCK: record calls for later verification
    NotificationService notifier = mock(NotificationService.class);

    TransferService service = new TransferService(notifier);
    service.transfer("ACC-001", "ACC-002", new BigDecimal("5000.00"));

    // VERIFY the interaction (behavior-based)
    verify(notifier, times(1)).sendEmail(
        eq("rajesh@nationalbank.in"),
        contains("Transfer of Rs 5,000.00")
    );
}

@Test
void failedTransferDoesNotNotify() {
    NotificationService notifier = mock(NotificationService.class);
    TransferService service = new TransferService(notifier);

    assertThrows(InsufficientBalanceException.class, () ->
        service.transfer("ACC-001", "ACC-002", new BigDecimal("9999999"))
    );

    // VERIFY: notification was NOT sent on failure
    verify(notifier, never()).sendEmail(anyString(), anyString());
}

What is the difference between a stub and a mock?

| Aspect | Stub | Mock |
|---|---|---|
| Purpose | Control behavior | Verify interaction |
| You configure | Return values | (also return values) |
| You assert | The result or state | The calls made |
| Question | "Given this input, is the output correct?" | "Did my code call the right thing?" |
| Example | Stub rate service to return 83.50 | Verify notification was sent once |

How does Mockito work?

Mockito creates test doubles for Java classes and interfaces.

JAVA
// Mockito basics
import static org.mockito.Mockito.*;

// Create a mock
AccountRepository repo = mock(AccountRepository.class);

// Stub a method
when(repo.findById("ACC-001")).thenReturn(
    new Account("ACC-001", new BigDecimal("50000.00"))
);

// Stub an exception
when(repo.findById("INVALID")).thenThrow(
    new AccountNotFoundException("INVALID")
);

// Verify interactions
verify(repo).save(any(Account.class));
verify(repo, times(2)).findById(anyString());
verify(repo, never()).delete(anyString());

// Capture arguments
ArgumentCaptor<Account> captor = ArgumentCaptor.forClass(Account.class);
verify(repo).save(captor.capture());
assertEquals(new BigDecimal("45000.00"), captor.getValue().getBalance());

How does WireMock work?

WireMock creates a fake HTTP server for mocking external REST APIs.

JAVA
// WireMock: mock an external fraud-check API
import com.github.tomakehurst.wiremock.client.WireMock;

@Test
void transferCallsFraudCheck() {
    // Stub the external API
    stubFor(post(urlEqualTo("/api/fraud-check"))
        .willReturn(aResponse()
            .withStatus(200)
            .withBody("{\"approved\": true}")
            .withHeader("Content-Type", "application/json")));

    // Run the code that calls the external API
    transferService.transfer("ACC-001", "ACC-002", new BigDecimal("50000"));

    // Verify the call was made with the right body
    WireMock.verify(postRequestedFor(urlEqualTo("/api/fraud-check"))
        .withRequestBody(containing("ACC-001")));
}

// Simulate a timeout
stubFor(post(urlEqualTo("/api/fraud-check"))
    .willReturn(aResponse()
        .withFixedDelay(30000))); // 30 second delay

// Simulate a server error
stubFor(post(urlEqualTo("/api/fraud-check"))
    .willReturn(aResponse().withStatus(500)));

What other test doubles exist?

| Double | Description | Example |
|---|---|---|
| Dummy | Passed but never used | A null logger in a constructor |
| Stub | Returns preset values | Rate service returns 83.50 |
| Mock | Records and verifies calls | Notification service call count |
| Fake | Lightweight working implementation | In-memory repository |
| Spy | Wraps a real object, records calls | Real service with call tracking |

When should you mock vs not mock?

Mock at the right boundary: mock external or slow collaborators, not the class under test.

TEXT
  DO mock:
  - External HTTP services (use WireMock)
  - Database in unit tests (use Mockito)
  - Email/notification services
  - Third-party APIs

  DO NOT mock:
  - The class you are testing
  - Simple value objects
  - Everything (over-mocking couples tests to implementation)
  - In integration tests (use the real thing)

What are common interview follow-up questions?

  • "When would you use a stub vs a mock?" Stub for state-based assertions (check the result). Mock when the interaction IS the behavior (verify the call).
  • "What is over-mocking?" Mocking everything couples tests to implementation detail, making refactoring painful. Mock only the external boundary.
  • "Can Mockito create stubs and mocks?" Yes. The same mock() object can both return stubbed values and be verified for interactions.

Key Takeaway

Mocking and stubbing are ways to replace a real dependency with a fake, a test double, so I can test a unit in isolation, fast, deterministic, and free of external systems. The distinction is intent. A stub is about controlling behavior: I configure it to return preset values or throw specific exceptions, so I control the conditions the code under test experiences, and then I assert on the state or output the code produces. For example I stub a rate service to return a fixed exchange rate so I can assert the converted amount, without caring how many times it was called. A mock is about verifying interaction: it records how it was called, and after exercising the code I verify that the expected calls happened, the right method, the right arguments, the right number of times. I use a mock when the correct behavior is the interaction itself, for example verifying that a successful transfer calls the notification service exactly once and a blocked transfer does not call it at all. In short, stubbing answers does my code produce the right output given this input from the dependency, and mocking answers did my code interact with its collaborators correctly.

The tools make this practical. Mockito is the standard Java library for creating doubles of classes and interfaces, where I stub methods with thenReturn or thenThrow and verify interactions with verify and times or never, plus argument matchers and captors. WireMock addresses the HTTP boundary, standing up a fake HTTP server I program to return specific responses, status codes, bodies, headers, delays, and faults, so I can test code that calls an external REST API deterministically, including error and slow responses.

The nuances I stress are to mock at the right boundary, the external or slow collaborators, not the class under test, and not to over-mock, because verifying too many interactions couples tests to implementation detail and makes refactoring painful, so I stub for state-based assertions and mock only when the interaction genuinely is the behavior worth asserting. I also keep the real thing in higher-level contract or integration tests, because mocks verify my code against my assumptions, which could be wrong. At the National Bank of India I stubbed the fraud service to drive both the allowed and blocked branches of the transfer logic, mocked the notifier to verify a successful transfer notified the customer exactly once and a blocked one never did, and used WireMock to simulate the exchange-rate API including a timeout so I could test how the code handled it. Being able to define stub versus mock precisely, control behavior versus verify interaction, and name Mockito and WireMock with how I use each is what shows an interviewer I write isolated, deterministic tests the right way.

Remember one sentence: a stub controls what the code sees by returning preset values, a mock verifies how the code interacts by recording calls, and you choose based on whether you assert state or interaction.

---

Visual Overview

DIAGRAM
  Test Doubles: Stub vs Mock
  ───────────────────────────

  STUB (control behavior)         MOCK (verify interaction)
  ┌──────────────────────┐       ┌──────────────────────┐
  │ Returns preset values │       │ Records calls made   │
  │                       │       │                       │
  │ rateService.getRate() │       │ notifier.send(email) │
  │   -> returns 74.5     │       │   -> recorded!       │
  │                       │       │                       │
  │ You assert: the       │       │ You verify: send was │
  │ converted amount is   │       │ called once with     │
  │ correct               │       │ rajesh@bank.in       │
  └──────────────────────┘       └──────────────────────┘

  Tools:
  Mockito -> Java objects (both stub and mock)
  WireMock -> HTTP services (fake server)
Share
Use ← → arrow keys to navigate