DEV Community

Cover image for Dependency Mocking vs Service Virtualization: What Actually Replaces Hand-Written Mocks in Integration Tests
Marx Jenes
Marx Jenes

Posted on

Dependency Mocking vs Service Virtualization: What Actually Replaces Hand-Written Mocks in Integration Tests

Somewhere around our third microservice, our hand-written mocks stopped being an asset and started being a liability nobody wanted to own. Every mock lived in its own file, written by whoever needed it at the time, matching whatever the dependency returned on the day it was written. Six months later, a payments service changed its error response shape, and every mock that simulated it kept returning the old shape forever, because nothing told the mocks the world had moved on. Our tests stayed green. Production did not.

That's the point where most teams start asking whether there's something better than hand-writing mocks one file at a time, and the two answers that usually come up, dependency mocking (tooling) and service virtualization, sound similar enough that people use them interchangeably. They're not the same thing, and picking the wrong one for your situation costs you real time.

What hand-written mocks actually cost you

Before comparing tools, it's worth being honest about why hand-written mocks break down at scale:

They drift. A mock is a snapshot of a dependency's behavior at the moment someone wrote it. Nothing keeps it in sync as the real dependency changes.
They're incomplete by construction. Someone writes a mock for the cases they thought to handle - success, one or two errors. Real dependencies fail in ways nobody imagined until it happens in production.
They don't scale with team size. Each new mock is a new maintenance surface. Multiply that across a team writing dozens of integration tests a week and you get an unmanaged pile of stale fixtures nobody trusts.
They're disconnected from real traffic. A mock represents what someone thinks a dependency does, not what it actually does across the range of real requests hitting it in production.

Both dependency mocking and service virtualization tools exist to fix some version of this, but they fix different parts of it.

Dependency mocking (as a tooling category)

Dependency mocking tools automate the creation and maintenance of mocks for individual dependencies - a specific API, a database call, a message queue. Instead of hand-writing a mock object, you use a library or framework that intercepts calls to a dependency and returns configured or recorded responses.

The better tools in this category address the drift problem directly by generating mocks from observed behavior rather than someone's manual guess - recording a dependency's actual responses and replaying them, rather than hand-typing what someone assumes the response looks like. This is a meaningfully different approach from classic hand-rolled mocking, even though both get called "mocking."

Where this fits best: testing one service's interaction with one or a few specific dependencies, in unit or narrow integration tests, where you want fast, deterministic, isolated tests and don't need a full simulated environment.

Where it falls short: it typically operates one dependency at a time. It doesn't give you a full simulated environment where multiple interdependent services behave consistently with each other - which is exactly the situation distributed systems testing runs into.

A few tools commonly used in this category, each taking a different approach to generating or managing mocks: WireMock (HTTP-level stubbing, widely used in the Java ecosystem), Mockoon (a desktop app for designing and running mock APIs manually), Pact (contract-testing focused, mocks generated from consumer-defined contracts rather than recorded traffic), and Keploy (generates mocks automatically from real API and database traffic captured at the network layer via eBPF, rather than hand-defined stubs or contracts).

Service virtualization

Service virtualization operates at a different scope: instead of mocking individual calls, it simulates entire dependent systems or environments - a whole downstream service, or a cluster of them, behaving consistently as a unit, the way the real system would across a full transaction.

This matters most in distributed systems, where a single test scenario might touch five or six services, some of which are unavailable in a test environment, rate-limited, expensive to call repeatedly, or owned by another team entirely. Service virtualization gives you a realistic stand-in for that whole dependency graph, not just one endpoint.

The stronger implementations of this approach are also built from recorded, real traffic rather than someone's hand-built simulation - capturing the actual request/response behavior of a dependency (or a whole set of them) and replaying it faithfully, including realistic latency, error rates, and edge cases that were never intentionally designed into a hand-written mock because nobody thought to.

On the tooling side, this space includes established enterprise platforms like Broadcom's Service Virtualization (formerly CA Service Virtualization) and Parasoft Virtualize, both of which let teams build simulated environments manually or from recorded interactions, and Hoverfly, an open-source option for capturing and simulating HTTP service behavior. Keploy also applies here on the recorded-traffic side specifically - since it captures real inter-service traffic via eBPF, it can generate the kind of realistic, environment-wide behavior simulation service virtualization is meant to provide, without needing a separately maintained virtual environment built by hand.

Where record-and-replay changes the comparison

The most useful development across both categories, in practice, is the shift away from hand-authored simulation entirely and toward capturing real traffic and replaying it as test infrastructure. This addresses the core weaknesses of manual mocking directly:

Drift stops being a maintenance burden when mocks are regenerated from actual captured traffic rather than manually updated by a person who has to remember to do it.
Coverage improves because recorded traffic includes the edge cases real usage produces - malformed payloads, unusual timing, rare error responses - not just the cases a person thought to write by hand.
CI determinism is preserved because replay is exact: the same recorded interaction plays back the same way every run, without depending on a live, possibly-flaky external dependency actually being reachable during a test.

This is the part of the space Keploy specifically focuses on: capturing real API traffic and database interactions directly at the network layer using eBPF, then generating both mocks and full integration/regression test cases from that captured traffic automatically, rather than requiring either hand-written mock objects or a separately maintained virtualization environment. Because the capture happens at the network layer, it works across languages and frameworks without instrumenting application code, and because the mocks are generated from what a dependency actually returned, they don't carry the "someone's best guess" problem that hand-written mocks always have.

The practical effect is that it blurs the line between the two categories in this comparison: it produces artifacts that function like dependency mocks (deterministic, fast, scoped per test) but are generated the way good service virtualization is built (from real recorded system behavior, not manual authorship), which is a meaningfully different position than tools that only do one half of that.

A rough way to choose

If you're testing a single service's handling of a small number of specific dependencies, and you mostly need fast, deterministic, isolated tests - dependency mocking tooling is usually the right scope, especially if it supports generating mocks from recorded traffic rather than manual definitions.

If you're testing across a distributed system where multiple services need to behave consistently together, and hand-maintaining that many mocks isn't realistic - you're in service virtualization territory, and the traffic-capture-based approaches are worth prioritizing over ones that require you to hand-build the virtual environment yourself.

Either way, the actual upgrade from hand-written mocks isn't really "mocking tool vs virtualization tool" as a binary choice. It's moving away from manually authored simulation of any kind and toward tests grounded in what your dependencies actually do, captured from real behavior instead of guessed at a keyboard.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.