-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
129 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,22 +13,33 @@ | |
Dominic Koepke | ||
Mail: [[email protected]](mailto:[email protected]) | ||
|
||
## License | ||
|
||
[BSL-1.0](LICENSE_1_0.txt) (free, open source) | ||
|
||
```text | ||
Copyright Dominic "DNKpp" Koepke 2024 - 2024 | ||
Distributed under the Boost Software License, Version 1.0. | ||
(See accompanying file LICENSE_1_0.txt or copy at | ||
https://www.boost.org/LICENSE_1_0.txt) | ||
``` | ||
# Table of Contents | ||
|
||
* [Introduction](#introduction) | ||
* [Core Design](#core-design) | ||
* [Examples](#examples) | ||
* [Other Choices](#other-choices) | ||
* [Special Acknowledgement](#special-acknowledgement) | ||
* [Customizability](#customizability) | ||
* [Stringification](#stringification) | ||
* [Integration](#integration) | ||
* [Installation](#installation) | ||
* [CMake](#cmake) | ||
* [Single-Header](#single-header) | ||
* [Test Framework](#test-framework) | ||
* [Documentation](#documentation) | ||
* [Testing](#testing) | ||
* [Windows](#windows) | ||
* [Linux](#linux) | ||
* [macOS](#macos) | ||
* [License](#license) | ||
* [Known Issues](#known-issues) | ||
|
||
--- | ||
|
||
## Introduction | ||
# Introduction | ||
|
||
``mimic++`` is a c++20 mocking framework, which aims to offer a very natural end expressive syntax, without constantly resorting to macros. | ||
``mimic++`` is a c++20 mocking framework, which aims to offer a natural end expressive syntax, without constantly resorting to macros. | ||
To be honest, macros cannot be completely avoided, but they can at least be reduced to a very minimum. | ||
|
||
The one thing, that this framework does different than all other (or at least all I am aware of) mocking framework is, that mock objects explicitly are function objects, | ||
|
@@ -40,7 +51,7 @@ So, ``mimicpp::Mock`` objects can directly be used as functional objects, but th | |
If you are curious, have a look at the [documentation](https://dnkpp.github.io/mimicpp/), investigate the examples folder or play around online at | ||
[godbolt.org](https://godbolt.org/z/dTWEhK15W). | ||
|
||
### Design Philosophy | ||
## Core Design | ||
|
||
The framework is designed with two core concepts in mind: Mocks and Expectations. | ||
Mocks can be used to define behaviour on a per test-case basis, without the necessity of creating dozens of types. The go-to example is, | ||
|
@@ -50,37 +61,57 @@ the so called "Expectations". | |
|
||
So, Mocks and Expectations are going together hand in hand. | ||
|
||
Additionally, users are able to create their own expectation policies easily and integrate them seamless into the rest of the framework. Be creative! | ||
|
||
#### Extensibility | ||
|
||
As already mentioned, users may invent their own expectations policies, but the framework doesn't stop there. | ||
In general this framework is designed to offer a robust foundation, which users can tailor for their needs. | ||
|
||
##### Stringification | ||
|
||
``mimic++`` can not provide stringification for any type out there, but it's often very useful to see a proper textual reprensentation of an object, when a test fails. | ||
``mimic++`` will use ``std::format`` for ``formattable`` types, but sometimes that is not, what we want, as users for example want to have an alternative | ||
stringification only for testing. | ||
Users can therefore add their specializations of the ``mimicpp::custom::Printer`` type and thus tell ``mimic++`` how a given type shall be printed. | ||
|
||
Custom specializations will always be prefered over any pre-existing printing methods, thus users may even override the stringification of e.g. ranges. | ||
|
||
##### Test Framework Integration | ||
|
||
Mocking frameworks usually do not exist for their own, as they are in fact just an advanced technice for creating tests. Instead, they should work | ||
together with any existing test framework out there. ``mimic++`` provides the ``IReporter`` interface, which in fact serves as a bridge from ``mimic++`` | ||
into the utilized test framework. ``mimic++`` provides some concrete reporter implementations for well known test frameworks, but users may create custom adapters for | ||
any test framework or simply use the default reporter. | ||
For more details have a look into the ``reporting`` section in the documentation. | ||
## Examples | ||
|
||
Official adapters exist for the following frameworks: | ||
```cpp | ||
#include <mimic++/mimic++.hpp> | ||
|
||
namespace matches = mimicpp::matches; | ||
using matches::_; | ||
namespace expect = mimicpp::expect; | ||
namespace finally = mimicpp::finally; | ||
|
||
TEST_CASE("Mocks are function objects.") | ||
{ | ||
mimicpp::Mock<int(std::string, std::optional<int>)> mock{}; // actually enables just `int operator ()(std::string, float)` | ||
SCOPED_EXP mock.expect_call("Hello, World", _) // requires the first argument to match the string "Hello, World"; the second has no restrictions | ||
and expect::at_least(1) // controls, how often the whole expectation must be matched | ||
and expect::arg<0>(!matches::range::is_empty()) // addtionally requires the first argument to be not empty (note the preceeding !) | ||
and expect::arg<1>(matches::ne(std::nullopt)) // requires the second argument to compare unequal to "std::nullopt" | ||
and expect::arg<1>(matches::lt(1337)) // and to be less than 1337 | ||
and finally::returns(42); // And, when matches, returns 42 | ||
|
||
int result = mock("Hello, World", 1336); // matches | ||
REQUIRES(42 == result); | ||
} | ||
|
||
TEST_CASE("Mocks can be overloaded.") | ||
{ | ||
mimicpp::Mock< | ||
int(std::string, std::optional<int>), // same as previous test | ||
void() const // enables `void operator ()() const` (note the const specification) | ||
> mock{}; | ||
|
||
SCOPED_EXP mock.expect_call() // setup an expectation for the void() overload | ||
and expect::twice(); // should be matched twice | ||
|
||
mock(); // first match | ||
|
||
// you can always create new expectations as you need them, even if the object is already in use | ||
SCOPED_EXP mock.expect_call(!matches::range::is_empty(), 42) // you can always apply matches directly; if just a value is provided, defaults to matches::eq | ||
and expect::once() // once() is the default, but you can state that explicitly | ||
and finally::throws(std::runtime_error{"some error"}); // when matches, throws an exception | ||
|
||
REQUIRE_THROWS(mock("Test", 42)); // ok, matched | ||
|
||
// still a pending expectation for void() overload | ||
std::as_const(mock)(); // explicitly call from a const object | ||
} | ||
``` | ||
* [Boost.Test](https://github.com/boostorg/test) | ||
* [Catch2](https://github.com/catchorg) | ||
* [GTest](https://github.com/google/googletest) | ||
## Other Choices | ||
#### Always Stay Within The Language Definition | ||
### Always Stay Within The Language Definition | ||
There are a lot of mocking frameworks, which utilize clever tricks and apply some compiler specific instructions to make the work more enjoyable. | ||
``mimic++`` does not! | ||
|
@@ -89,50 +120,43 @@ want to support and maintain over a set of compilers or configurations. | |
Unfortunatle this often leads to a less elegant syntax for users. If you need that, than this framework is probably not the right for you. | ||
Pick your poison :) | ||
### Basic Examples | ||
## Special Acknowledgement | ||
Mocks themselves are very easy to create: | ||
```cpp | ||
mimicpp::Mock<void()> myMock{}; | ||
``` | ||
This already is a fully functional Mock, which enables a member ``void operator()`` for which Expectations can be created. | ||
This framework is highly inspired by the well known [trompeloeil](https://github.com/rollbear/trompeloeil), which I have used myself for several years now. | ||
It's definitly not bad, but sometimes feels a little bit dated and some macros do not play very well with formatting tools and the like. | ||
If you need a pre-c++20 mocking-framework, you should definitly give it a try. | ||
```cpp | ||
mimicpp::ScopedExpectation myExpectation = myMock.expect_call(); | ||
``` | ||
The ``expect_call()`` member function initiates an expectation. Expectations are usually required to be fulfilled within the current (or deeper) scope. | ||
The ``ScopedExpectation`` then takes over the responsibility to check the Expectation, when that scope is left. Usually users do not need direct | ||
access to the expectations but still an unique name is required. To overcome that language limitation, an optional macro can be used: | ||
```cpp | ||
SCOPED_EXP myMock.expect_call(); | ||
``` | ||
This effectively does the same job as before, but the macro takes over the burden creating an unique name for that expectation. | ||
Fun fact: ``mimic++`` uses ``trompeloeil`` for it's own test suite :D | ||
Given the previously created expectation, it is expected, that the call operator of ``myMock`` is called exactly once: | ||
```cpp | ||
myMock(); | ||
``` | ||
# Customizability | ||
Expectations can contain several requirements, e.g. ``times`` which indicates, how often an Expectation must be met. | ||
For more examples, have a look into the documentation or directly into the ``examples`` directory. | ||
A framework should be a useful tool that can be used in a variety of ways. However, it should not be a foundation that limits the house to be built on it. | ||
For this reason ``mimic++`` offers various ways for customization: E.g. users may create their own expectation policies and integrate them seamlessly, without | ||
changing any line of the ``mimic++`` code-base. | ||
### Special Acknowledgement | ||
## Stringification | ||
This framework is highly inspired by the well known [trompeloeil](https://github.com/rollbear/trompeloeil), which I have used myself for several years now. | ||
It's definitly not bad, but sometimes feels a little bit dated and some macros do not play very well with formatting tools and the like. | ||
If you need a pre-c++20 mocking-framework, you should definitly give it a try. | ||
``mimic++`` can not provide stringification for any type out there, but it's often very useful to see a proper textual reprensentation of an object, when a test fails. | ||
``mimic++`` will use ``std::format`` for ``formattable`` types, but sometimes that is not, what we want, as users for example want to have an alternative | ||
stringification just for testing purposes. | ||
Users can therefore add their specializations of the ``mimicpp::custom::Printer`` type and thus tell ``mimic++`` how a given type shall be printed. | ||
Fun fact: ``mimic++`` uses ``trompeloeil`` for it's own test suite :D | ||
Custom specializations will always be prefered over any pre-existing printing methods, thus users may even override the stringification of the internal report types. | ||
## Documentation | ||
The documenation is generated via ``doxygen``. Users can do this locally by enabling both, the ``MIMICPP_CONFIGURE_DOXYGEN`` and ``MIMICPP_CONFIGURE_DOXYGEN``, | ||
The documenation is generated via ``doxygen``. Users can do this locally by enabling both, the ``MIMICPP_CONFIGURE_DOXYGEN`` and ``MIMICPP_ENABLE_GENERATE_DOCS``, | ||
cmake options and building the target ``mimicpp-generate-docs`` manually. | ||
The documentation for the ``main`` branch is always available on the github pages; for the ``development`` branch it is also available on the ``dev-gh-pages`` branch, | ||
The documentation for the ``main`` branch is always available on the [github-pages](https://dnkpp.github.io/mimicpp/); for the ``development`` branch it is also available on the ``dev-gh-pages`` branch, | ||
but unfortunatly not directly viewable on the browser. | ||
Every release has the generated documentation attached. | ||
# Integration | ||
``mimic++`` is a head-only library. Users can easily enjoy all features by simply including the ``mimic++/mimic++.hpp`` header. Of course one can be more granular | ||
and include just what's necessary. The choice is yours. | ||
## Installation | ||
### CMake | ||
|
@@ -171,11 +195,25 @@ CPMAddPackage("gh:DNKpp/mimicpp#<any_commit_hash_or_tag>") | |
|
||
### Single-Header | ||
|
||
Each release has a header file named ``mimic++-amalgamated.hpp`` attached, which contains all definitions (except for the specific test-framework adapters) | ||
and can be simply dropped into any c++20 project. | ||
After that, users may also just pick the appropriate adapter header for their used test-framework and put that into their project aswell. | ||
As an alternative each release has a header file named ``mimic++-amalgamated.hpp`` attached, which contains all | ||
definitions (except for the specific test-framework adapters) and can be simply dropped into any c++20 project (or used on [godbolt.org](https://godbolt.org). | ||
After that, users may also just pick the appropriate adapter header for their desired test-framework and put that into their project aswell. | ||
|
||
## Test Framework | ||
|
||
Mocking frameworks usually do not exist for their own, as they are in fact just an advanced technice for creating tests. Instead, they should work | ||
together with any existing test framework out there. ``mimic++`` provides the ``IReporter`` interface, which in fact serves as a bridge from ``mimic++`` | ||
into the utilized test framework. ``mimic++`` provides some concrete reporter implementations for well known test frameworks, but users may create custom adapters for | ||
any test framework or simply use the default reporter. | ||
For more details have a look into the ``reporting`` section in the documentation. | ||
|
||
Official adapters exist for the following frameworks: | ||
|
||
* [Boost.Test](https://github.com/boostorg/test) | ||
* [Catch2](https://github.com/catchorg) | ||
* [GTest](https://github.com/google/googletest) | ||
|
||
## Testing | ||
# Testing | ||
|
||
``mimic++`` utilizes a strict testing policy, thus each official feature is well tested. The effect of those test-cases are always tracked by the extensive ci, | ||
which checks the compilation success, test cases outcomes and coverage on dozens of different os, compiler and build configurations. | ||
|
@@ -185,14 +223,14 @@ The coverage is generated via ``gcov`` and evaluated by | |
[codecov](https://codecov.io/gh/DNKpp/mimicpp) and | ||
[coveralls](https://coveralls.io/github/DNKpp/mimicpp). | ||
|
||
### Windows | ||
## Windows | ||
|
||
| OS | Compiler | c++-20 | c++-23 | | ||
|--------------|----------|:------:|:------:| | ||
| Windows 2022 | msvc | x | x | | ||
| Windows 2022 | clangCl | x | x | | ||
|
||
### Linux | ||
## Linux | ||
|
||
| Compiler | libstdc++ | libc++ | c++-20 | c++-23 | | ||
|----------|:---------:|:------:|:------:|:------:| | ||
|
@@ -201,7 +239,7 @@ The coverage is generated via ``gcov`` and evaluated by | |
| gcc-13 | x | - | x | x | | ||
| gcc-14 | x | - | x | x | | ||
|
||
### MacOs | ||
## macOS | ||
|
||
| Compiler | libstdc++ | libc++ | c++-20 | c++-23 | | ||
|-------------------|:---------:|:------:|:------:|:------:| | ||
|
@@ -210,7 +248,20 @@ The coverage is generated via ``gcov`` and evaluated by | |
|
||
As new compilers become available, they will be added to the workflow, but older compilers will probably never be supported. | ||
|
||
## Known Issues | ||
# License | ||
|
||
[BSL-1.0](LICENSE_1_0.txt) (free, open source) | ||
|
||
```text | ||
Copyright Dominic "DNKpp" Koepke 2024 - 2024 | ||
Distributed under the Boost Software License, Version 1.0. | ||
(See accompanying file LICENSE_1_0.txt or copy at | ||
https://www.boost.org/LICENSE_1_0.txt) | ||
``` | ||
|
||
--- | ||
|
||
# Known Issues | ||
|
||
### Clang-18.1 + libc++ | ||
Date: 25.09.2024 | ||
|