🛠️ Testing, Tools & Ecosystem · Advanced

Mocking with Mockito in Java

Mocks vs stubs vs fakes, when/thenReturn, verify, not mocking what you don't own.

🧩 The mysteryYou want to test the checkout, but every run charges a real credit card and emails a real customer. Hollywood solved this problem long ago: stunt doubles.

Meet the doubles

A test double stands in for a real dependency. A stub returns canned answers. A mock records calls so you can verify them. A fake is a simple working version, like an in-memory repository. A spy wraps a real object and overrides some calls.

Script it, then check it

Mockito creates doubles with **mock(). when(...).thenReturn(...) scripts answers; verify(...)** checks what was called.

UserRepo repo = mock(UserRepo.class);
when(repo.findName(1)).thenReturn("Ana");
 
var greeter = new Greeter(repo);
assertEquals("Hi Ana", greeter.greet(1));
verify(repo).findName(1);
🔮 Predict it

Nothing stubbed

repo is a mock and nothing is stubbed. What does repo.findAll() (returning List<User>) return?

  1. An empty list
  2. null
  3. Throws UnsupportedOperationException
Show the answer

An empty list. Unstubbed methods return harmless defaults: empty collections, empty Optionals, 0, false, or null for other objects. So tests only stub what matters to them.

⚠️ The trap

Matchers are all or nothing

Once one argument uses a matcher like anyString(), every argument must be a matcher. The raw 3 makes Mockito throw InvalidUseOfMatchersException. Write **eq(3)** instead.

when(pricing.quote(anyString(), 3))   // bad
    .thenReturn(99.0);
when(pricing.quote(anyString(), eq(3))) // ok
    .thenReturn(99.0);

Verifying counts

verify runs after the code under test and checks interactions. **times(n)** asserts an exact count; never() and atLeastOnce() exist too. when() only sets up answers; it checks nothing.

checkout.placeOrder(order);
verify(mailer, times(1)).send(any());
verify(audit, never()).alert(any());

Don't mock what you don't own

✗ Mocking a library
HttpClient http = mock(HttpClient.class);
when(http.send(any(), any()))
    .thenReturn(fakeResponse);

Encodes your guesses about a foreign API's behavior.

✓ Mocking your wrapper
PaymentGateway gw = mock(PaymentGateway.class);
when(gw.charge(order)).thenReturn(OK);

Your own small interface; test the real wrapper with integration tests.

💼 In the real world

Over-mocking

A suite where everything is mocked can be green while production is on fire: the mocks agree with your assumptions, not with reality. Mock at boundaries you own (payments, mail, clock), keep real objects inside, and back the boundaries with a few integration tests.

Key takeaways

  1. Stub = canned answers; mock = verify interactions; fake = simple real version
  2. If one argument uses a matcher, all of them must
  3. verify(mock, times(n)).method(...) checks calls
  4. Don't mock what you don't own: wrap it first
🤯 Did you know?

Mockito builds its mocks at runtime with the ByteBuddy bytecode library, generating classes on the fly, much like frameworks do for class-based proxies.

Practice questions

repo is a Mockito mock and nothing is stubbed. What does `repo.findAll()` (returning List<User>) return?

  1. Throws UnsupportedOperationException
  2. A list with one mock User
  3. null
  4. An empty list
Check your answer

An empty list. Mockito's default answer returns empty collections, empty Optionals, 0, false or null, so unstubbed calls rarely crash a test.

You must check that exactly one email is sent when an order is placed. Which Mockito call fits?

  1. mock(Mailer.class, times(1))
  2. when(mailer.send(any())).thenReturn(true)
  3. verify(mailer, times(1)).send(any())
  4. assertEquals(1, mailer)
Check your answer

verify(mailer, times(1)).send(any()). verify checks interactions after the code has run; times(1) asserts the exact count. Stubbing with when() only sets up answers.

Mocks help you test code you've already written. What if you wrote the test first? Next: test-driven development.