Library / SDK
rollbear/trompeloeil avatar
rollbear/trompeloeil

Trompeloeil: expectations and side effects for C++ mocks, header only

Header only C++14 mocking framework

885 stars95 forksC++BSL-1.0

At a glance

What is it?
Trompeloeil is a header-only C++ mocking framework that expresses call expectations as a chain of modifiers, so a REQUIRE_CALL can demand a return value, a side effect, a call order and a call count in one statement. It runs on the Boost Software License and supports GCC from 4.8.4 upwards through Visual Studio 2019.
Who is it for?
Adopt Trompeloeil when you need to assert not just that a collaborator was called but how it was called, since the modifier chain covers return values, captured parameters, ordering and counts in one expectation, and the header-only design means there is nothing to build or link. Do not adopt it if your team wants a mock object you can hand to code that expects a concrete non-virtual interface, since the framework mocks virtual members through MAKE_MOCK.
Can I use it commercially?
Yes. BSL-1.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 89 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • rollbear/trompeloeil885 ★ · +3
Star history
Repository2026-10-222026-11-07
rollbear/trompeloeil882885

3 Stars in the last 16 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

A mock is a class of MAKE_MOCK declarations

Trompeloeil calls itself a thread-safe header-only mocking framework for C++11 and 14 using the Boost Software License 1.0, and its first use is declaring a mock class. The interface you want to fake is written out as an abstract class with pure virtual functions, and the mock derives from it, declaring one MAKE_MOCK or MAKE_MOCK0 per member. The signature is given as an auto template argument, so the compiler checks the mock against the base class and a mismatched signature becomes a compile error rather than a runtime surprise.

Cpp
class Mock : public Interface
{
public:
  MAKE_MOCK(foo, auto (int, std::string&) -> bool, override);
  MAKE_MOCK(bar, auto (int) -> bool, override);
  MAKE_MOCK(bar, auto (std::string) -> bool, override);
  MAKE_MOCK0(baz, void()); // not from Interface
};

The MAKE_MOCK0 variant covers a member with no arguments and no return value, which is why the comment in the example notes that baz is not part of Interface. Overloaded members are declared once per overload, as bar is here. Because the framework mocks virtual members, the mock is usable anywhere the interface pointer is expected, which is the point of deriving from it.

Expectations are modifier chains, not separate calls

The distinctive part of the API is that a single expectation carries everything about a call. REQUIRE_CALL takes the mock and the call, and the modifiers that follow refine it: RETURN sets the value, SIDE_EFFECT and LR_SIDE_EFFECT write to captured variables, WITH narrows the argument match, IN_SEQUENCE orders the call, and TIMES sets a count.

Cpp
  REQUIRE_CALL(m, bar(ANY(int)))     // expect call to m.bar(int)
    .LR_SIDE_EFFECT(local_var = _1)  // set captured variable to value of param
    .RETURN(_1 > 0)                  // return value depending on param value
    .IN_SEQUENCE(seq1)               // must be first match for seq1
    .TIMES(AT_LEAST(1));             // can be called several times

  FORBID_CALL(m, bar(0));            // but m.bar(0) is not allowed

The underscore is the wildcard matching any value, and numbered underscores refer to specific parameters, so the side effect can copy the argument that was actually passed into a local variable. AT_LEAST and the surrounding count matchers live in the trompeloeil namespace along with the wildcard. FORBID_CALL is the negative form: it states that a particular call must not happen, which catches the case where a code path calls the method with an argument value you did not mean to allow.

Sequences let you constrain call order

Ordering is expressed with a sequence object, and the example declares two of them. IN_SEQUENCE(seq1) means the call must be the first match for that sequence, and IN_SEQUENCE(seq1, seq2) means the call must be last for both. So a single expectation can sit at the front of one ordering and the back of another, which is how you describe a pipeline where one call must happen before another but the final call closes both.

Cpp
  REQUIRE_CALL(m, foo(gt(2), _))     // expect call to foo(int,std::string&)
    .WITH(_2 == "")                  // with int > 2 and empty string
    .IN_SEQUENCE(seq1, seq2)         // last for both seq1 and seq2
    .SIDE_EFFECT(_2 = "cat")         // and set param string to "cat"
    .RETURN(true);

The gt matcher is a greater-than comparison on an argument, and WITH combines it with the wildcard. SIDE_EFFECT on a by-reference parameter writes through it, so the mock can hand a value back to the caller. The project links to a separate post on sequence control for examples of restricting or relaxing the allowed sequences of matching calls, which suggests this is the part of the framework that takes the most reading.

Building the self test suite needs CMake options

The project ships its own test suite, and the build is controlled by CMake options. You run cmake with -DTROMPELOEIL_BUILD_TESTS=yes, use CXX_STANDARD to choose which C++ standard to test, and SANITIZE to pick sanitizers. The README notes that the self tests need a reasonably modern CMake.

code
cmake -B build_dir \
      -D TROMPELOEIL_BUILD_TESTS=yes \
      -D CMAKE_BUILD_TYPE=Debug \
      -D CXX_STANDARD=17 \
      -D SANITIZE=Address,Undefined \
      <trompeloeil source dir>

After the build, individual test targets are built and then run.

code
cmake --build build_dir -t self_test thread_terror custom_recursive_mutex

The target names hint at what the suite covers: thread_terror is the thread safety tests, and custom_recursive_mutex exercises a custom mutex type. The last piece is the test framework itself. If the build finds a CMake package for Catch2 it will use that, otherwise it downloads a header-only version of Catch2 2.x, so a contributor without Catch2 installed still gets a working build at the cost of a network fetch.

The compiler support floor is old, and that is a feature

The compatibility list is one of the more remarkable things about this project. Trompeloeil is known to work with GCC 4.8.4 and up, then 4.9.3, and versions 5 through 13; with Clang 3.5 through 17, so 3.6, 3.7, 3.8, 3.9, then 4 up to 17; and with Visual Studio 2015, 2017 and 2019. Latest patch level releases are assumed in the versions listed. GCC 4.8.4 is from 2014, which means the framework is expected to compile in an embedded or legacy corporate toolchain where the alternatives, which rely on newer language features, will not build. That is a real and unusual constraint to design for, and it is also why the documentation links to a page on backward compatibility with earlier versions of C++. The repository carries a separate page on platform and library support, and there is a check_errors.sh script and a compilation_errors directory, which suggests the project actively tracks which compilers reject which constructs.

Header only, but Conan and CMake for distribution

There is nothing to compile, since the framework is header only and the whole implementation lives in include with a single header. Distribution is still handled properly. The README carries a Conan badge pointing at the Conan Center, and the repository ships a trompeloeil-config.cmake file so the package can be found by find_package, plus a CMakeLists.txt at the root and a LICENSE_1_0.txt file matching the Boost Software License 1.0 the project declares. The licence field on the repository says BSL-1.0. There is also an install_libcxx.sh script, which fits a project that supports Clang on macOS, where the standard library ships separately. For editor integration, the README notes that the ReSharperC++ extension to Visual Studio has had a mock generator for Trompeloeil since 2016.2, so the MAKE_MOCK declarations can be produced by the IDE rather than typed by hand.

Where Trompeloeil is the wrong choice

Three limitations apply. First, it mocks virtual members, so a class with non-virtual methods, or a free function, cannot be faked with it without first refactoring the code under test to call through an interface. That refactoring is often worth doing, but it is work you have to plan for rather than something the framework absorbs. Second, the C++ standard requirement cuts both ways: supporting GCC 4.8.4 limits the framework to C++11 and 14 features, so the modern template tricks that newer mocking libraries use are off the table, and that constraint will eventually show in what the library can offer. Third, the version numbers are not semantic. The tags are v47, v48 and v49, published on 2024-01-01, 2024-07-14 and 2024-10-07, so there is no indication of what changed between them and you cannot depend on a major number for compatibility. The last push to the repository was on 2026-08-11, so the code is being touched, but the published releases are two years older than that.

Contributing follows a branch and issue policy

The contribution rules are spelled out in the README rather than left to a separate file. For new functionality you are asked to file an issue as an enhancement request first, to get a discussion going about how to implement it, and the same is good practice for bug fixes so others can see when a problem is solved. The ChangeLog is said to reflect what a user can see, with internal changes normally omitted, although performance improvements and silenced warnings are visible to users and do get mentioned. There is also a branch routing table that tells you where each kind of change goes.

code
|Documentation                |master branch   |
|Trivial bugfixes             |master branch   |
|Non trivial bugfixes         |develop branch  |
|Simple new functionality     |develop branch  |
|Non-trivial new functionality|new topic branch|

A small convention sits inside that: feel free to add your name to the copyright blurb. The project also keeps a cheat sheet in PDF form, a Cook Book, a FAQ, a reference page and a page on backward compatibility, plus a set of presentation videos including the introduction talk and a session on using the framework for modern C++.

Editorial conclusion

Adopt Trompeloeil when you need to assert not just that a collaborator was called but how it was called, since the modifier chain covers return values, captured parameters, ordering and counts in one expectation, and the header-only design means there is nothing to build or link. Do not adopt it if your team wants a mock object you can hand to code that expects a concrete non-virtual interface, since the framework mocks virtual members through MAKE_MOCK. Verify first that your compiler is on the supported list, that GCC 4.8.4 or Clang 3.5 is the oldest you need to support, and that Catch2 is available or can be fetched, since the self test suite prefers a CMake package for it and otherwise downloads a header-only Catch2 2.x.

Frequently asked questions

What is Trompeloeil and what does it need?

It is a thread-safe header-only mocking framework for C++11 and 14 using the Boost Software License 1.0. There is nothing to build or link, since the implementation is header only.

How do I declare a mock in Trompeloeil?

Derive from the interface you want to fake and declare one MAKE_MOCK per member, giving the signature as an auto template argument, with MAKE_MOCK0 for a member that takes no arguments and returns void. The signature is checked against the base class by the compiler.

How do I check the order of calls in Trompeloeil?

Declare sequence objects and attach them with IN_SEQUENCE. A call with IN_SEQUENCE(seq1) must be the first match for that sequence, and passing two sequences makes the call the last match for both. The README also links to a post on restricting or relaxing allowed call sequences.

Which compilers does Trompeloeil support?

The README lists GCC 4.8.4 and up, 4.9.3 and versions 5 through 13, Clang 3.5 through 17, and Visual Studio 2015, 2017 and 2019, assuming the latest patch releases of each. The project also links to pages on backward compatibility with earlier C++ versions and on platform and library support.

How do I build the Trompeloeil self test suite?

Run cmake with -D TROMPELOEIL_BUILD_TESTS=yes, and use CXX_STANDARD and SANITIZE to choose the standard and sanitizers. If the build finds a CMake package for Catch2 it uses that, otherwise it downloads a header-only Catch2 2.x.

Official sources

  1. Issues
  2. License: BSL-1.0
  3. README
  4. Releases
  5. rollbear/trompeloeil on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rollbear-trompeloeil.svg)](https://hysenlabs.com/projects/rollbear-trompeloeil)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/rollbear-trompeloeil.svg)](https://hysenlabs.com/projects/rollbear-trompeloeil)

Was this useful?