Testing Without Mocks

goloroden1 pts0 comments

Testing Without Mocks - EventSourcingDB

Skip to content

Initializing search

Getting Started

Fundamentals

Deployment and Operations

Reference

Client SDKs

Extensions

MCP Server

Best Practices

Implementation and Development

Data Management and Performance

Operations, Compliance and Infrastructure

Common Issues

Blog

Categories

Privacy Policy

Legal Notice

Testing Without Mocks¶

Open the test suite of almost any business application and start counting the mocks. A mock for the repository, a mock for the clock, a mock for the email gateway, a mock for the payment provider. By the time the test finally reaches the code it was meant to check, half of the system has been replaced by stand-ins that you wrote yourself. The test runs, it turns green, and it tells you something. The question is what.

Here is the uncomfortable part: a test that passes because of its mocks can be worse than no test at all. No test leaves you knowingly uncertain. A green mock-driven test hands you confidence that nobody has any right to give you, because nothing guarantees that the mock still resembles the real thing. Mocks are not a testing strategy. They are a workaround for two problems: domain logic that is tangled up with infrastructure, and infrastructure that is too slow to touch in a test. Event Sourcing removes the first. A database that starts in milliseconds removes the second. This is what testing looks like once you stop pretending.

Why We Reach for Mocks¶

Mocks did not appear out of nowhere. They solve a real problem. In a typical state-based system, your business logic is entangled with the things it depends on. The code that decides whether an order may ship also loads the order from a database, asks a clock for the current time, and calls a shipping service. You cannot exercise the decision without dragging all of that along. So you replace each dependency with a mock, and suddenly the decision is testable in isolation.

That much is reasonable. The trouble starts with what you are left asserting. When behavior hides inside state mutations and side effects, there is often no return value that captures what happened – so the test falls back on the only thing it can see: which methods were called, in which order, with which arguments. You stop testing what the system did and start testing how it was wired.

This is the tautology trap. You tell the mock how to behave, you run the code, and then you assert that the code did what you just told the mock to expect. The test passes by construction. It will keep passing even if the real dependency changed its contract last week, because the mock never got the memo.

And that is the deeper danger. A test that turns green because of its mocks is not neutral; it actively misleads. A missing test leaves a visible gap, and everyone knows the code is unverified. A green test that only confirms its own mocks looks like proof. The problem begins the moment someone realizes that nobody ever guaranteed the mock reflects reality. By then the suite has been vouching for code it never actually exercised, and the bug it was supposed to catch is already in production.

The Domain Becomes a Function¶

Event Sourcing changes the shape of the problem. A command handler in an event-sourced system does not load mutable state and poke at it. It takes the events that have already happened, looks at the command, and returns the new events that should follow. Its entire job is to answer one question: given this history, what happens next?

We explored the underlying structure in Decide, Evolve, Repeat : a decide function with the signature (command, state) -> events, where the state itself is folded from past events. Whether you express it as a decider, a functional core, or a plain method, the essence is the same. The decision is a pure function of events in and events out. It reads nothing, writes nothing, and depends on nothing but its arguments.

Compare that with the state-based handler from earlier. To decide whether an order may ship, it had to load the order, ask a clock for the time, and reach out to a shipping service – three collaborators, three mocks. In an event-sourced handler those concerns simply are not there. The history arrives as an argument, the time the decision depends on is passed in as a value rather than pulled from a clock it has to call, and talking to other systems is somebody else's job, handled at the edges where no decision is made. There is nothing left in the middle to fake.

A pure function is the easiest thing in the world to test. There is no clock to freeze, no repository to fake, no service to intercept. You hand it values and you inspect the values it returns. There is, quite literally, nothing to mock, because there is no collaborator to stand in for.

Given, When, Then, and Nothing Else¶

Because the handler is pure, tests fall into a shape that reads like a sentence: given a history of past events, when a command...

test mock mocks testing nothing events

Related Articles