Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/docs
diff options
context:
space:
mode:
authorLeitwolf2026-11-01 21:19:33 +0300
committerGitHub2026-11-01 21:19:33 +0300
commit5cbd481125b881a4c2eed74050be9899eb3f23a7 (patch)
tree496d26ff659e92cdf4e6fe27a4aa47bf52818052 /docs
parent26a1588ae3ae183ae5c900caf3c294bdcb3f8855 (diff)
parent8603d31fd8c1a72e30cb444f278d7a0592383ba7 (diff)
downloadcatch3-5cbd481125b881a4c2eed74050be9899eb3f23a7.tar.gz
Merge pull request #30 from Progmasoft/feature/mocking
Add mocks that report through the assertion path
Diffstat (limited to 'docs')
-rw-r--r--docs/Readme.md1
-rw-r--r--docs/progmasoft-mocking.md142
-rw-r--r--docs/progmasoft-roadmap.md11
-rw-r--r--docs/why-catch3.md3
4 files changed, 151 insertions, 6 deletions
diff --git a/docs/Readme.md b/docs/Readme.md
index 3da1a8ca..15fe412d 100644
--- a/docs/Readme.md
+++ b/docs/Readme.md
@@ -38,6 +38,7 @@ Catch2 release.
**Running:**
* [The Catch3 runner](progmasoft-runner.md#top)
+* [Mocking](progmasoft-mocking.md#top)
* [Command line reference](command-line.md#top)
* [Running specific section/generator](filtering-execution-path.md#top)
diff --git a/docs/progmasoft-mocking.md b/docs/progmasoft-mocking.md
new file mode 100644
index 00000000..f79a537e
--- /dev/null
+++ b/docs/progmasoft-mocking.md
@@ -0,0 +1,142 @@
+<a id="top"></a>
+
+# Mocking
+
+`<Progmasoft/Catch3/Mock.hpp>` replaces a dependency of the code under test
+with a mock that checks how it is called. Mocks report through Catch's
+assertion path: an unexpected call, a call out of order and an unmet
+expectation fail the test case that owns the mock, at the line that declared
+the expectation, in every report the runner writes.
+
+```cpp
+#include <Progmasoft/Catch3/Mock.hpp>
+#include <Progmasoft/Catch3/TestMacros.hpp>
+
+class Store
+{
+public:
+ virtual ~Store() = default;
+ virtual int Get(const std::string &key) const = 0;
+ virtual void Put(const std::string &key, int value) = 0;
+};
+
+class MockStore final : public Store
+{
+public:
+ CATCH3_MOCK_METHOD(int, Get, (const std::string &), const override);
+ CATCH3_MOCK_METHOD(void, Put, (const std::string &, int), override);
+};
+
+TEST_CASE("A score goes up by one")
+{
+ MockStore store;
+ CATCH3_EXPECT_CALL(store.GetMock)("ada").Returns(41);
+ CATCH3_EXPECT_CALL(store.PutMock)("ada", 42);
+
+ AddPoint(store, "ada");
+}
+```
+
+A mock verifies itself in its destructor, so nothing has to be called at the
+end of the test case. When the test case is already being left by a failure or
+a skip, the expectations it did not reach are not reported on top of it.
+`Verify()` checks the expectations earlier and forgets them, for a test case
+that goes on with new ones.
+
+## Declaring mocks
+
+`CATCH3_MOCK_METHOD(result, name, (types), specifiers)` declares the member
+function `name` and the mock `nameMock` beside it. The specifiers are whatever
+follows the parameters: `const`, `noexcept`, `override`. Up to 8 parameters are
+supported, and a type with a comma in it, such as `std::map<std::string, int>`,
+needs an alias.
+
+Overloads need mocks with names of their own:
+
+```cpp
+CATCH3_MOCK_OVERLOAD(void, Put, PutOne, (const std::string &, int), override);
+CATCH3_MOCK_OVERLOAD(void, Put, PutAll, (const Scores &), override);
+```
+
+`CATCH3_MOCK_FUNCTION(name, signature)` declares a `MockFunction` on its own.
+
+## Setting expectations
+
+`CATCH3_EXPECT_CALL(mock)(arguments...)` expects one call. Without arguments it
+accepts any arguments; otherwise each parameter takes one of these:
+
+| Written as | Accepts |
+| --- | --- |
+| a value | what compares equal to it; strings compare by value, also as `const char *`, and integers by value whatever their types, so 256 never accepts an `unsigned char` |
+| a Catch matcher, such as `StartsWith("a")` | what the matcher accepts |
+| `Any` | anything |
+| `Where(predicate, "description")` | what the predicate accepts |
+
+A composition such as `StartsWith("a") && EndsWith("z")` refers to temporaries
+that are gone when the mock is called, and Catch's vector matchers, such as
+`Equals(vector)`, refer to their vector. Neither compiles here; put them in a
+`Where`. A matcher of your own has to hold what it compares with.
+
+The result of `CATCH3_EXPECT_CALL` takes these, in any order:
+
+| Member | Meaning |
+| --- | --- |
+| `Times(n)`, `Between(a, b)`, `AtLeast(n)`, `AtMost(n)`, `AnyTimes()`, `Never()` | how many calls it wants; one by default |
+| `Returns(value)` | the result of each call; a type that cannot be copied moves out on the first one, and a reference result refers to `value` |
+| `Does(function)` | runs `function` with the arguments, and returns what it returns; it keeps its state between calls |
+| `Throws(exception)` | throws `exception` from each call |
+| `InSequence(sequence)` | see below |
+
+A call goes to the first expectation, in the order they were declared, that
+accepts its arguments and can take another call. A call none of them accepts
+fails the test case and returns a value-initialized result. So does a call they
+accept when all of them are used up. When the result cannot be made that way, a
+reference for example, the test case ends there; the same happens to an
+expectation with no `Returns` or `Does`. A `noexcept` function cannot leave the
+test case that way, so such a call ends the process.
+
+A mock with no expectation for a call treats it as a failure. An expectation
+with `AnyTimes()` is how to allow calls that do not matter to the test.
+
+## Sequences
+
+Expectations in a `Sequence` have to be met in the order they were declared in,
+across mocks:
+
+```cpp
+Sequence order;
+CATCH3_EXPECT_CALL(file.OpenMock)().InSequence(order);
+CATCH3_EXPECT_CALL(file.WriteMock)(Any).AtLeast(1).InSequence(order);
+CATCH3_EXPECT_CALL(log.FlushMock)().InSequence(order);
+```
+
+A step that is passed takes no more calls. A step reached before the ones ahead
+of it have their calls fails the test case and names them.
+
+## Free functions
+
+Code that reaches a free function through a seam, a `std::function` or a
+function pointer it calls through, can be pointed at a mock. `ScopedSeam` puts
+the seam back at the end of its scope:
+
+```cpp
+CATCH3_MOCK_FUNCTION(clock, long());
+CATCH3_EXPECT_CALL(clock)().Returns(50L);
+
+const ScopedSeam seam(clockSeam, clock.AsFunction());
+CHECK(SecondsSince(20) == 30);
+```
+
+The mock has to outlive what `AsFunction` returns. A function pointer can only
+take a function without state, so it reaches a mock of static lifetime through
+one.
+
+## Threads
+
+Mocks count and order calls correctly when several threads call them. Reports
+go through Catch's assertions, which are not safe across threads unless
+`CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS` is set, so a failure in a
+thread other than the test's needs that setting.
+
+Outside of a test case, such as in the destructor of a global, there is nothing
+to fail: failures go to standard error.
diff --git a/docs/progmasoft-roadmap.md b/docs/progmasoft-roadmap.md
index 5ab04264..81a8956f 100644
--- a/docs/progmasoft-roadmap.md
+++ b/docs/progmasoft-roadmap.md
@@ -21,7 +21,7 @@ The steps are ordered by what each one builds on.
| --- | --- | --- |
| 1 | Native property testing: choice-sequence shrinking, combinators, state machines, statistics, targeted search, example database, differential and metamorphic oracles | done; see [Native property testing](progmasoft-properties.md#top) |
| 2 | The Catch3 runner | done; see [The Catch3 runner](progmasoft-runner.md#top) |
-| 3 | Mocking | planned |
+| 3 | Mocking | done; see [Mocking](progmasoft-mocking.md#top) |
| 4 | Per-test code coverage | planned |
| 5 | Change-impact test selection and test prioritization | planned |
| 6 | Mutation testing | planned |
@@ -56,10 +56,11 @@ Catch3 runner replaced Catch2's in every test binary:
## 3. Mocking
-Mocks that report through Catch2's assertion path: expectations with call
-counts, argument matchers built on Catch2's matchers, sequences, and actions,
-for virtual interfaces and for free functions behind a seam. Unexpected calls
-and unmet expectations fail the test case that owns the mock.
+Mocks report through Catch2's assertion path: expectations with call counts,
+argument matchers built on Catch2's matchers, sequences, and actions, for
+virtual interfaces and for free functions behind a seam. Unexpected calls and
+unmet expectations fail the test case that owns the mock. See
+[Mocking](progmasoft-mocking.md#top).
## 4. Per-test code coverage
diff --git a/docs/why-catch3.md b/docs/why-catch3.md
index 120295f0..871b6efc 100644
--- a/docs/why-catch3.md
+++ b/docs/why-catch3.md
@@ -20,13 +20,14 @@ Catch2's runner with [its own](progmasoft-runner.md#top).
| C++ baseline | C++17; the headers work in C++17 and later | The same |
| Randomness | Catch2's existing behavior is unchanged | A specified SplitMix64 sequence provides deterministic trial seeds and unbiased bounded integer sampling |
| Generated values | No change to existing Catch2 generators | Strategies for numbers, text, collections and structures with `Map`, `Filter` and `FlatMap`; shrinking of recorded choices, so every combination shrinks; replay tokens and an example database; state machines, `Classify`/`Cover` statistics, targeted search, and differential and metamorphic oracles |
+| Mocks | None | [`CATCH3_MOCK_METHOD`, `CATCH3_EXPECT_CALL`](progmasoft-mocking.md#top) and `Sequence`, reporting through the assertion path |
| Build identity | One library: the `Catch3` CMake package with `Catch3::Catch3` and `Catch3::Catch3WithMain`, `//:catch3` in Bazel, `catch3` in Meson and pkg-config | The same library |
| Source terms | Catch2-originated source retains its upstream BSL-1.0 terms | Progmasoft-authored additions state their own SPDX license in each file |
Properties take any number of generated values. Shrinking is deterministic and
bounded, but local rather than globally minimal. Parallel trials do not
exist yet; [the roadmap](progmasoft-roadmap.md#top)
-orders them with mocking, per-test coverage, test selection, mutation testing
+orders them with per-test coverage, test selection, mutation testing
and test generation.
## Compatibility policy