Mocking and Fakes in Scala: Mastering Test Isolation with Mockito and ScalaMock

Explore mocking and fakes in Scala with emphasis on choosing the lightest test double that preserves behavior clarity, especially in functional and capability-oriented code.

Test doubles: Substitute collaborators used in tests, such as mocks, stubs, spies, and fakes, to isolate the code under test from external dependencies or behaviors.

In Scala, the most important question is usually not “which mocking library should we use?” It is “do we need a mock at all?” Many Scala designs are easier to test with fakes, small interpreters, or plain function values than with interaction-heavy mocking.

Prefer the Lightest Double That Tells the Truth

Common options, from simpler to more controlling:

  • plain values or functions
  • stubs returning fixed responses
  • in-memory fakes
  • spies recording calls
  • mocks with interaction expectations

The lighter the double, the less likely the test is to overfit the current implementation.

Fakes Often Fit Scala Better Than Mocks

If your code depends on an algebra or small capability trait, an in-memory fake is often clearer:

1trait UserRepo:
2  def save(user: User): Unit
3  def all(): Vector[User]
4
5final class InMemoryUserRepo extends UserRepo:
6  private var users = Vector.empty[User]
7  def save(user: User): Unit = users = users :+ user
8  def all(): Vector[User] = users

That lets the test assert behavior through resulting state rather than through a fragile sequence of interactions.

Mocks Still Have a Place

Mocks are most useful when the interaction itself is the contract:

  • did we send a message exactly once?
  • did we avoid calling an expensive dependency on invalid input?
  • did the workflow emit a specific command to a boundary?

Those are real behavioral questions. The problem starts when every test becomes a transcript of internal method calls.

Mockito and ScalaMock

Libraries such as Mockito and ScalaMock can be useful, especially in mixed Java/Scala codebases or legacy object-oriented modules. But they should support the test design, not define it.

If a simple fake or function would express the boundary more clearly, prefer that over a heavy expectation framework.

Common Failure Modes

Mocking the World

Every collaborator is mocked, and the test stops describing behavior in terms the domain actually cares about.

Verifying Incidental Calls

Tests fail after harmless refactors because they were tied to call order or helper method structure rather than the real contract.

Using Mocks to Compensate for Hidden Dependencies

The code under test is too coupled, and mocks are being used as scaffolding to survive that design rather than as a focused testing tool.

Practical Heuristics

Prefer pure functions, stubs, and fakes where possible. Use mocks when interaction is genuinely the behavior under test, and keep expectations narrow enough that a refactor does not look like a bug.

Knowledge Check

Loading quiz…
Revised on Thursday, April 23, 2026