Skip to content

Commit

Permalink
docs: overhaul readme.md
Browse files Browse the repository at this point in the history
  • Loading branch information
DNKpp committed Sep 26, 2024
1 parent 9caff9d commit a3d27dc
Showing 1 changed file with 129 additions and 78 deletions.
207 changes: 129 additions & 78 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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!
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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 |
|----------|:---------:|:------:|:------:|:------:|
Expand All @@ -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 |
|-------------------|:---------:|:------:|:------:|:------:|
Expand All @@ -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
Expand Down

0 comments on commit a3d27dc

Please sign in to comment.