skip to content

In Mockito, what is the difference between a mock and a spy?

level: juniorimportance: must knowfreq 78%

answer

  1. Mock = no real code, returns defaults
  2. Spy = real object, real code unless stubbed
  3. Partial mocking = override only some methods
  4. Stub spies with doReturn().when(), not when().thenReturn()
  5. Mock to isolate, spy to keep most real behavior

basics

~20 s

A mock is a fake object with no real behavior; every method returns a default (null, 0, false) until you stub it. A spy wraps a real object and runs its real code unless you stub a specific method.

solid answer

~40 s

A Mockito mock is a completely fake stand-in: it has no real implementation, so every method call returns a type-based default (null for objects, 0 for ints, false for booleans, empty collections) until you stub it with when(...).thenReturn(...). A spy, created with Mockito.spy(realObject) or @Spy, wraps a real instance and delegates to the actual methods by default, recording the calls. You only override the methods you explicitly stub, which is called partial mocking. Use a mock when you want to fully control and isolate a dependency; use a spy when you want most of the real behavior but need to override or verify a few methods. A common gotcha: stub spies with doReturn(...).when(spy).method() rather than when(spy.method()).thenReturn(...), because the latter actually calls the real method first.

code

java · 14 lines
java
// Mock: no real behavior, returns null/0/false until stubbed
List<String> mockList = Mockito.mock(List.class);
System.out.println(mockList.size());      // 0 (default, real code never runs)
when(mockList.size()).thenReturn(5);
System.out.println(mockList.size());      // 5 (stubbed)

// Spy: wraps a REAL ArrayList, runs real methods by default
List<String> spyList = Mockito.spy(new ArrayList<>());
spyList.add("a");                          // real add runs
System.out.println(spyList.size());        // 1 (real behavior)

// Safe spy stubbing: doReturn(...).when(...) never calls the real method
doReturn(100).when(spyList).size();
System.out.println(spyList.size());        // 100 (overridden)

go deeper

for a junior

States the one-line difference: mock = fake/no real behavior/returns defaults; spy = real object running real code unless stubbed. Knows mock() vs spy().

for a middle

Explains partial mocking, when to pick each, and recognizes the doReturn().when() safety rule for spies even if shaky on why.

for a senior

Articulates WHY when(spy.foo()) calls the real method (argument-evaluation order), names default-value semantics precisely, and advises mock-by-default with spies as a smell to investigate.

for a principal

Frames it against the xUnit double taxonomy, discusses testability/design implications (over-reliance on spies signaling SRP violations or untestable legacy code), and sets team conventions on when partial mocking is acceptable.

## What problem mocking solves In unit testing you isolate the **class under test** from its **collaborators** (dependencies it talks to: a repository, an HTTP client, a clock). Instead of using the real collaborator you substitute a **test double** — a stand-in object you control. Mockito is the most popular Java library for creating these doubles. The two main kinds it produces are **mocks** and **spies**. ## Mock — a fully fake object A **mock** is created with `Mockito.mock(SomeClass.class)` or the `@Mock` annotation. Mockito generates a subclass/proxy of the type at runtime where **none of the real code runs**. Every method is replaced. Until you tell it otherwise, each method returns a **default value** based on its return type: - object types → `null` - `int`/`long`/`double` etc. → `0` - `boolean` → `false` - collections (`List`, `Set`, `Map`) → empty collection (a Mockito convenience) You then **stub** behavior — define what a method should return — with `when(mock.foo()).thenReturn(value)`. You **verify** interactions with `verify(mock).foo()`. The key idea: a mock has **no real behavior at all**, so the test is fully isolated and deterministic. ## Spy — a wrapper over a real object A **spy** is created with `Mockito.spy(realObject)` or `@Spy`. A spy holds a **real instance** and, by default, **delegates every call to the real method** — the actual code executes. The spy additionally **records** every call so you can `verify(...)` it, and lets you **selectively override** individual methods. Overriding only some methods of an otherwise-real object is called **partial mocking**. So the default behavior is the headline difference: | | Mock | Spy | |---|---|---| | Underlying object | none (all fake) | a real instance | | Default method behavior | returns type default | runs the real method | | You stub… | the methods you need | only the methods you want to override | | Typical use | isolate a dependency | keep real behavior, tweak/verify a bit | ## The `doReturn().when()` gotcha With a **mock**, both stubbing styles work: ```java when(mock.getValue()).thenReturn(42); // fine on a mock ``` With a **spy**, that same line is dangerous. Because Java evaluates arguments before calling a method, `mock.getValue()` (here a spy) is **actually invoked for real** before `when(...)` ever sees it. If the real method throws (e.g. hits a null field or a DB), your stubbing line blows up. The safe form is: ```java doReturn(42).when(spy).getValue(); // does NOT call the real method ``` Here `when(spy)` returns a stubbing proxy and `.getValue()` is intercepted, not executed. Rule of thumb: **always stub spies (and any method with risky side effects) with `doReturn(...).when(...)`.** ## When to choose which - **Mock** by default — it gives the cleanest isolation and forces you to think about exactly which interactions matter. - **Spy** only when you genuinely need most of a class's real behavior but want to stub/verify a slice of it — e.g. testing a method on a class while overriding one helper it calls. Heavy reliance on spies often signals a class doing too much; consider splitting it instead. ## Related notions Mockito's `mock`/`spy` are *technical* tools. The classic xUnit vocabulary (Gerard Meszaros) distinguishes **dummy, fake, stub, spy, mock** by *role*. Mockito blurs these — a Mockito "mock" can act as a stub (canned answers) or a mock (verifies interactions). Don't over-index on the textbook taxonomy in interviews; explain Mockito's two concrete behaviors clearly.

  • Why does when(spy.foo()).thenReturn(x) misbehave but the same works on a mock?
    Java evaluates spy.foo() before when(...) sees it, so on a spy the real foo() actually runs (and may throw). On a mock there is no real method, so nothing harmful happens. Use doReturn(x).when(spy).foo() to avoid invoking the real method.
  • If you create a spy from new ArrayList<>() and never stub anything, what does size() return after two real adds?
    2 — a spy delegates to the real object, so the real add and size run.

A mock is a cardboard cutout of a person — looks like them but does nothing until you script it. A spy is the real person wearing a wire: they act normally, you record everything, and you can occasionally feed them a line to say.

saying these in an interview costs you the question

  • Saying a spy is 'just a mock with a real name' — the defining difference is that a spy runs real code by default while a mock never does.
  • Claiming when().thenReturn() never works on spies — it works for safe methods, it's just unsafe when the real method has side effects or throws.
  • Thinking a mock returns the real object's values — a fresh mock returns type defaults, not real behavior.
  • Believing a spy stubs all methods automatically — only the ones you explicitly override are changed.

context