mimic++ v6
|
#### Quality Badges
#### Developer Badges
Test Framework
mimic++
is a C++20 mocking framework designed to offer a natural and expressive syntax. While many similar frameworks aim for this goal, they often rely heavily on an extensive list of macros. Although macros can be beneficial, mimic++
strives to minimize their usage to enhance readability and maintainability.
What sets mimic++
apart from other mocking frameworks is its unique approach: Mock objects are explicitly defined as function objects, making them directly callable and overloadable. This design choice ensures that the framework remains extensible and straightforward to use.
If you're curious to learn more, feel free to explore the documentation and the cheat-sheet of to the latest release. You can also check out the examples folder or experiment with the framework online at godbolt.org.
The framework is built around two core concepts: Mocks and Expectations.
Mocks are objects that simulate functional implementations, allowing you to set up behavior on a per-test-case basis. These behaviors are referred to as Expectations, which define whether and how the mock should respond during testing.
The framework diligently tracks all Expectations and reports any that are either violated or left unfulfilled, providing useful information for debugging. This tracking mechanism helps developers quickly identify issues in their tests, ensuring that the intended behavior is accurately represented and maintained.
In essence, Mocks and Expectations work hand in hand to facilitate effective testing.
As mentioned earlier, mimicpp::Mock
objects are actual function objects and can be used directly.
mimicpp::Mock
also supports arbitrary overload-sets, enabling an operator()
for each specified signature.
mimicpp::Mock
objects can also be used as member functions. However, this approach has its limitations; for example, they cannot be used via a member function pointer.
mimic++
also provides utilities for mocking interfaces.
Sometimes, an interface method may also have several overloads. mimic++
directly supports overriding overload-sets.
The mimicpp::Watched
helper can report the destruction and relocation of object instances.
mimic++
is designed to work with any C++20 conforming compiler, independent of the underlying platform or architecture. This is achieved by consistently adhering to the language standards, which is continuously verified through an extensive CI/CD workflow that tracks numerous configurations.
In fact, mimic++
is known to work on Windows, Ubuntu, and macOS with both x86_64
and x86_32
architectures. For a more comprehensive overview, please refer to the Testing section.
This framework is heavily inspired by the well-known trompeloeil, which I have personally used for several years. While it is definitely a solid choice, it can sometimes feel a bit dated, and some macros may not work well with formatting tools and similar utilities. If you need a pre-C++20 mocking framework, I highly recommend giving it a try.
Fun fact: mimic++
uses trompeloeil
for its own test suite :D
A framework should be a versatile tool that can be utilized in various ways and tailored to meet specific needs. For this reason, mimic++
offers a range of customization options. For example, users can create their own expectation policies and integrate them seamlessly without modifying any line of the mimic++
codebase.
mimic++
cannot provide stringification for every type, but having a proper textual representation of an object can be very useful when a test fails. mimic++
will use std::format
(or fmt) for types that are formattable, but sometimes that may not meet users' needs, as they might prefer an alternative stringification specifically for testing purposes.
To address this, users can add their own specializations of the mimicpp::custom::Printer
type, allowing them to specify how a given type should be printed. Custom specializations will always take precedence over any pre-existing printing methods, enabling users to override the stringification of internal report types as well.
Matchers are used to check whether arguments satisfy specific requirements. While there are many existing matchers available, users often have unique needs.
mimic++
provides a very generic mimicpp::PredicateMatcher
, which is often sufficient for most cases. However, if you need full control, you can start with a fresh type (without any inheritance) and build your own. Custom matchers simply need to conform to the mimicpp::matcher_for
concept. For more information, please refer to the documentation.
There are multiple types of policies, depending on the tasks they are designed to fulfill. The expectation policy has full control over whether a match can be made or should be rejected, while the finalize policy determines what a mock should do when it actually matches (such as returning a value or throwing an exception).
These policies can implement arbitrary logic, so feel free to experiment. There is no base type requirement; they simply need to satisfy either the mimicpp::expectation_policy_for
, mimicpp::control_policy
, or mimicpp::finalize_policy_for
.
If you are working with a large framework, there’s a good chance that it utilizes a custom string or character type ( such as QChar
and QString
from Qt). While they may appear different, they are essentially just strings, so it would be beneficial to make them fully compatible with the existing string matchers.
mimic++
supports this; users simply need to provide some trait-specializations. For more information, please refer to the string section of the documentation.
Call conventions are a somewhat controversial topic, as the C++ language definition does not explicitly address them. However, frameworks like Microsoft's COM utilize the __stdcall
call convention, indicating that at least some compilers support these specifications. Consequently, users need the ability to leverage these features.
Since call conventions are not universally portable, mimic++
does not define any conventions itself. Instead, it provides an easy macro tool, MIMICPP_REGISTER_CALL_CONVENTION
, which users can utilize to make the framework compatible with any call convention they require.
The documentation is generated using Doxygen. Users can generate it locally by enabling both the MIMICPP_CONFIGURE_DOXYGEN
and MIMICPP_ENABLE_GENERATE_DOCS
CMake options, and then manually building the target mimicpp-generate-docs
.
The documentation for the main branch is always available on GitHub Pages. For the development branch, it is also available on the dev-gh-pages branch, but unfortunately, it is not directly viewable in the browser.
Each release includes the generated documentation as an attachment.
mimic++
is a header-only library, allowing users to easily access all features by simply including the mimic++/mimic++.hpp
header. Of course, users can also take a more granular approach and include only what is necessary. The choice is yours.
The integration into a cmake project is straight-forward.
Users can either select a commit in the main branch or a version tag and utilize the CMake FetchContent
module:
As an alternative, I recommend using CPM, which is a convenient wrapper based on the FetchContent
feature:
mimic++
also has a vcpkg port, which can be found here: As an alternative, each release includes a header file named mimic++-amalgamated.hpp
, which contains all definitions (except for the specific test framework adapters) and can be easily dropped into any C++20 project. After that, users can simply select the appropriate adapter header from the adapters
-folder and include it in their project as well.
Mocking frameworks typically do not exist in isolation; rather, they are advanced techniques for creating tests. They should work seamlessly with any existing test framework. Therefore, mimic++
provides the IReporter
interface, which serves as a bridge from mimic++
to the utilized test framework.
mimic++
already brings some existing reporter implementations for well-known test frameworks, but users can also create custom adapters for any test framework or simply use the default reporter. For more details, please refer to the reporting section in the documentation.
Official adapters exist for the following frameworks:
mimic++
employs a strict testing policy, ensuring that each official feature is thoroughly tested. The results of these test cases are consistently tracked by an extensive CI system, which checks compilation success, test case outcomes, and coverage across dozens of different operating systems, compilers, and build configurations.
For the test builds, the flags -Wall -Wextra -Wpedantic -Werror
(or /W4 /WX
on MSVC) are set. This ensures that mimic++
won't flood your build output with endless warnings - or, even worse, break your builds — if you enable these flags in your own projects.
The coverage is generated via gcov
and evaluated by codacy, codecov and coveralls.
Nevertheless, even with significant effort, achieving 100% code coverage remains out of reach. I strive to cover each branch, but the coverage percentage can vary between different tools. The goal is to get as close to 100% as possible.
On the other hand, there is a significant amount of code that isn't even analyzed by these tools, such as templates and macros. mimic++
contains a lot of templated code at its core, which requires at least an equal amount of effort to get right and thoroughly tested. Therefore, it's important to take the coverage percentage with a grain of salt.
The listed configurations are explicitly tested, but other do probably work, too. As new compilers become available, they will be added to the workflow, but older compilers will probably never be supported.
Symbol | Description |
---|---|
x | works |
* | works with caveats |
- | does not work |
? | not tested |
Windows
OS | Compiler | x86_32 | x86_64 | c++-20 | c++-23 | formatting | stacktrace |
---|---|---|---|---|---|---|---|
Windows 2022 | msvc | x | x | x | x | std/fmt | std/cpptrace |
Windows 2022 | clangCl | x | x | x | x | std/fmt | std/cpptrace |
Linux
Compiler | x86_32 | x86_64 | libstdc++ | libc++ | c++-20 | c++-23 | formatting | stacktrace |
---|---|---|---|---|---|---|---|---|
clang-16 | x | x | x | x | x | x | fmt | cpptrace |
clang-17 | x | x | x | x | x | x | std/fmt | std*/cpptrace |
clang-18 | x | x | x | * | x | x | std/fmt | std*/cpptrace |
clang-19 | x | x | x | x | x | x | std/fmt | std*/cpptrace |
gcc-12 | x | x | x | ? | x | x | fmt | cpptrace |
gcc-13 | x | x | x | ? | x | x | std/fmt | std*/cpptrace |
gcc-14 | x | x | x | ? | x | x | std/fmt | std*/cpptrace |
Note: libc++
doesn't support std::stacktrace
yet.
macOS
Compiler | x86_64 | libstdc++ | libc++ | c++-20 | c++-23 | formatting | stacktrace |
---|---|---|---|---|---|---|---|
AppleClang-16.0.6 | x | ? | x | x | x | fmt | cpptrace |
AppleClang-17.0.6 | x | ? | x | x | x | std/fmt | std*/cpptrace |
AppleClang-18.1.6 | x | ? | x | x | x | std/fmt | std*/cpptrace |
Note: macOS officially doesn't support 32bit builds, so they are not tested.
Date: 25.09.2024
This combination introduced a regression regarding the std::invocable
concept and a default parameter of type std::source_location
. On this version, all invocable checks will fail, but the std::is_invocable
trait still works as expected. Unfortunately this can not solved easily by this framework - sorry for that.
Clang-17 and Clang-19 do not suffer from this issue. For more information have a look here.