MediumConcept

What is mocking in testing and when should you use it?

Technology:
Software Testing
Experience:
Junior,
Mid-level

Quick answer

Mocking replaces a real dependency with a controllable fake so a test can run in isolation and check how the code interacts with it; use it for slow, non-deterministic or external dependencies, not for everything.

Detailed explanation

Test doubles come in a few flavours. A stub returns canned answers, a mock also records calls so you can assert on them, a spy wraps a real function to observe it, and a fake is a working lightweight implementation such as an in-memory repository.

Mock at the boundaries of your system: payment providers, email services, the clock, random numbers, network calls. That keeps tests fast and deterministic and lets you simulate failures that are hard to trigger for real, such as a timeout.

Over-mocking is the common mistake. When a test mocks every collaborator and asserts on internal calls, it breaks on every refactor while still passing when the real integration is broken. Prefer asserting on outputs and observable behaviour, and cover real integrations with a smaller set of integration tests.

Example

javascript
test('sends a receipt after payment', async () => {
  const email = { send: jest.fn().mockResolvedValue(undefined) };
  const payments = { charge: jest.fn().mockResolvedValue({ id: 'pay_1' }) };

  await checkout({ email, payments }, { userId: 7, total: 50 });

  expect(payments.charge).toHaveBeenCalledWith(7, 50);
  expect(email.send).toHaveBeenCalledWith(7, expect.stringContaining('pay_1'));
});

Key points

  • Stub, mock, spy and fake are different kinds of test doubles
  • Mock external, slow or non-deterministic dependencies
  • Over-mocking couples tests to implementation details

Common mistakes

  • Mocking the module under test, so the test proves nothing.
  • Asserting on every internal call instead of on results.
  • Mocking the database in all tests and never testing real queries.

Follow-up questions

  • What is dependency injection and how does it help testing?
  • Software TestingEasy

    What is the difference between unit, integration and end-to-end tests?

    Unit tests check one small piece of code in isolation, integration tests check that several pieces work together (for example code plus a real database), and end-to-end tests drive the whole application the way a user would.

    Intern 路 Junior 路 Testing
  • Software TestingEasy

    What is the test pyramid?

    The test pyramid is a guideline to have many fast unit tests at the base, fewer integration tests in the middle and only a small number of slow end-to-end tests at the top, so the suite stays fast and reliable.

    Junior 路 Mid-level 路 Testing
  • Software TestingMedium

    How do you deal with flaky tests?

    Treat a flaky test as a bug: quarantine it so it stops blocking the pipeline, reproduce it by running it repeatedly, fix the root cause (usually timing, shared state or external dependencies), then bring it back.

    Mid-level 路 Senior 路 Testing
  • Software TestingMedium

    Selenium vs Cypress vs Playwright: which should you choose for test automation?

    Selenium is the long-established, language-agnostic standard built on WebDriver; Cypress runs inside the browser with an excellent developer experience for JavaScript apps; Playwright offers fast, auto-waiting cross-browser automation with multiple tabs, contexts and languages. For a new web project, Playwright is usually the strongest default.

    Mid-level 路 Senior 路 Testing
  • PythonMedium

    What are decorators in Python and how do they work?

    A decorator is a function that takes another function and returns a modified version of it, letting you add behaviour such as logging, caching or access control without changing the original code.

    Mid-level 路 Senior 路 Functions & Scope