C++ mocking framework architecture

C/C++ Mocking Framework Architecture: Typemock Isolator++ (Part 3)

A C++ mocking framework is supposed to make unit testing easier, but in real C++ systems, it often becomes harder: static calls, non-virtual methods, third-party binaries, and code you can’t safely refactor. Typemock Isolator++ is a C++ mocking framework built for that reality, enabling isolation at runtime so you can test legacy code without changing production code.

In this part of the Typemock Architecture series, we’ll explain, at a high level, how Isolator++ makes mocking the unmockable possible in C++ and why the v5 engine is built for modern CI, Linux, and real-world codebases.


What makes C++ mocking fundamentally hard

Most C++ mocking tools assume you designed for testability (interfaces everywhere, dependency injection, wrappers, link-time tricks). But production C++ rarely looks like that:

  • Global/static functions with side effects
  • Third-party libraries you can’t change
  • Legacy code with tight coupling
  • Complex call stacks and exceptions
  • Multi-threaded test runs in CI

So the core question becomes:
How do you isolate behavior without rewriting production code?


The Isolator++ approach: intercept at runtime

Isolator++ doesn’t depend on “nice architecture.” It operates at runtime, at the level where the compiled code actually runs.

That means:

  • No forced interfaces
  • No source rewriting
  • No “just refactor it” requirement
  • The interception is active only during test execution

✅ Typemock is built for teams shipping real C++ code, not textbook examples.


How the Typemock C++ mocking framework works at runtime

Test runner starts the test process

  • Isolator++ loads into the test process
  • A configured call is intercepted
  • The engine chooses: Return, DoInstead, or CallOriginal
  • Test finishes and the process exits (no persistent hooks)

Nothing is modified on disk.
Nothing remains after the test process exits.


Why “just refactor it” often isn’t realistic in C/C++

When someone says “Just remove statics” or “Make it virtual so you can mock it”, they’re assuming you own the design and can safely change it. In real systems, that’s often false, and in C++ it can be painfully expensive or dangerous.

1) ABI and binary compatibility

Turning a non-virtual method into a virtual one can change the object layout (vtable pointer, layout/order of members), which can break:

  • binary compatibility across DLLs/shared libraries
  • plugins loaded at runtime
  • third-party components compiled against older headers

Even “small” changes can trigger crashes that only appear in production configurations.

2) You don’t control the code

A huge chunk of static/non-virtual calls come from:

  • third-party libraries
  • legacy internal libraries used by multiple products
  • OS APIs / vendor SDKs
    You can’t refactor them, and wrapping them everywhere is time-consuming and brittle.

3) Wide blast radius (static calls are everywhere)

Static calls often end up scattered across the codebase:

Refactoring this to DI means touching many files, changing many constructors, and creating a long migration period where everything is half-old / half-new.

4) Inlining, headers, and templates amplify the pain

In C++, many non-virtual functions are:

  • inline in headers
  • template-based
  • compiled into multiple translation units

Refactoring becomes a full rebuild, linker debugging, and performance risk, not a small change.

5) Behavioral risk: statics often hide state

Statics may include:

  • caches
  • singletons
  • global configuration
  • lazy initialization
  • hidden threading constraints

Changing them can introduce subtle race conditions or initialization-order bugs.

6) Performance and determinism requirements

Some systems avoid virtual dispatch intentionally:

  • Low-latency trading
  • Realtime or embedded systems
  • Allocation-free or deterministic code paths

Adding abstraction layers (interfaces, heap-injected dependencies) can violate performance budgets or memory constraints.


Before and after: what refactoring really looks like

Before (typical legacy)

The refactor you’re being asked to do

This is “clean” – but it’s also a lot of change, and sometimes it breaks ABI, performance, or release timelines.


What Typemock enables because of this reality

This is exactly why Typemock Isolator++ exists:

✅ You can unit test now, even when the code is not refactor-friendly.
✅ You can isolate static/global/non-virtual behavior without redesigning your system first.
✅ You can refactor later when it’s safe, not because a testing tool forced you into it.


The trampoline engine (high-level)

At the heart of Isolator++ is a “redirect and decide” mechanism often described as a trampoline:

  1. Execution reaches a function you want to fake
  2. The engine redirects control to a safe handler
  3. The handler decides what to do
  4. Execution returns to the test (or to the original function)

This design is what enables:

  • Mocking global/static/free functions
  • Intercepting calls across module boundaries
  • Handling hard-to-test dependencies without rewriting

Why v5 matters: Windown, Linux, GCC and safer unwinding

Modern C++ teams run CI on Linux, across compilers, with lots of parallelism. Isolator++ v5 is designed for that environment:

  • Windows Support (VS 15 and above)
  • Linux support (GCC 5.4 and above)
  • Modern API (C++14 baseline)

C++ mocking framework examples: from unmockable to mockable

Example 1: Mocking a static method

Production code (unchanged):

Test using Isolator++:

No interfaces.
No wrappers.
No refactor required.


Example 2: Mocking a global / free function

This works even if SendPacket lives in another library or is locally copied.


Example 3: Replacing behavior with WillDoInstead

This is extremely powerful for:

  • simulating failures
  • edge cases
  • complex behaviors
  • time-dependent or flaky dependencies

Where this fits in the full Typemock architecture

Isolator++ is one layer in the broader Typemock system:

  • Mocking / Isolation engine (this post)
  • SmartRunner (impact-based test running)
  • Coverage (instant coverage + reporting)
  • Deep IDE integration (especially Visual Studio)
typemock architecture engine

This is what allows a team to go from “we can’t unit test this” to “tests run fast, in isolation, continuously.”


Test frameworks commonly used with a C++ mocking framework

Most teams using a C++ mocking framework like Typemock run their tests through standard, well-known test runners:

Typemock Isolator++ works with all of these runners, loading only inside the test process and acting as a C++ mocking framework at runtime without requiring changes to production code.


Next in the series

Part 4: coming soon (what changes the economics of unit testing).
Part 1: How the .NET Isolation Engine works
Part 2: Inside the .NET Isolator Engine

Download: Isolator++ (C++ mocking framework trial)