From d4ae1b18c0c22b6b8653ccbac4c5a0902fb4546b Mon Sep 17 00:00:00 2001 From: Phil Nash Date: Tue, 14 Feb 2017 09:15:21 +0000 Subject: [PATCH] Matcher documentation --- docs/assertions.md | 4 +- docs/matchers.md | 102 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 docs/matchers.md diff --git a/docs/assertions.md b/docs/assertions.md index ef8d32af..d3a90478 100644 --- a/docs/assertions.md +++ b/docs/assertions.md @@ -105,13 +105,11 @@ REQUIRE_NOTHROW([&](){ ## Matcher expressions -To support Matchers a slightly different form is used. Matchers will be more fully documented elsewhere. *Note that Matchers are still at early stage development and are subject to change.* +To support Matchers a slightly different form is used. Matchers have [their own documentation](matchers.md). * **REQUIRE_THAT(** _lhs_, _matcher expression_ **)** and * **CHECK_THAT(** _lhs_, _matcher expression_ **)** -Currently only string matchers are implemented and consist of: `Contains`, `Equals`, `StartsWith` and `EndsWith`. - Matchers can be composed using `&&`, `||` and `!` operators. --- diff --git a/docs/matchers.md b/docs/matchers.md new file mode 100644 index 00000000..e3ad64aa --- /dev/null +++ b/docs/matchers.md @@ -0,0 +1,102 @@ +# Matchers + +Matchers are an alternative way to do assertions which are easily extensible and composable. +This makes them well suited to use with more complex types (such as collections) or your own custom types. +Matchers were first popularised by the [Hamcrest](https://en.wikipedia.org/wiki/Hamcrest) family of frameworks. + +## In use + +Matchers are introduced with the `REQUIRE_THAT` or `CHECK_THAT` macros, which take two arguments. +The first argument is the thing (object or value) under test. The second part is a match _expression_, +which consists of either a single matcher or one or more matchers combined using `&&`, `||` or `!` operators. + +For example, to assert that a string ends with a certain substring: + + ```c++ +std::string str = getStringFromSomewhere(); +REQUIRE_THAT( str, EndsWith( "as a service" ) ); + ``` + +The matcher objects can take multiple arguments, allowing more fine tuning. +The built-in string matchers, for example, take a second argument specifying whether the comparison is +case sensitive or not: + +```c++ +REQUIRE_THAT( str, EndsWith( "as a service", Catch::CaseSensitive::No ) ); + ``` + +And matchers can be combined: + +```c++ +REQUIRE_THAT( str, + EndsWith( "as a service" ) || + (StartsWith( "Big data" ) && !Contains( "web scale" ) ) ); +``` + +## Built in matchers +Currently only a few string matchers are built-in: `StartsWith`, `EndsWith`, and `Contains` and `Equals`. +These each take an optional second argument for case sensitivity (defaulting to case sensitive). +More matchers will be coming - for example for testing elements in a vector. + +## Custom matchers +It's easy to provide your own matchers to extend Catch or just to work with your own types. + +You need to provide two things: +1. A matcher class, derived from `Catch::MatcherBase` - where `T` is the type being tested. +The constructor takes and stores any arguments needed (e.g. something to compare against) and you must +override two methods: `match()` and `describe()`. +2. A simple builder function. This is what is actually called from the test code and allows overloading. + +Here's an example for asserting that an integer falls within a given range +(note that it is all inline for the sake of keeping the example short): + +```c++ +// The matcher class +class IntRange : public Catch::MatcherBase { + int m_begin, m_end; +public: + IntRange( int begin, int end ) : m_begin( begin ), m_end( end ) {} + + // Performs the test for this matcher + virtual bool match( int const& i ) const override { + return i >= m_begin && i <= m_end; + } + + // Produces a string describing what this matcher does. It should + // include any provided data (the begin/ end in this case) and + // be written as if it were stating a fact (in the output it will be + // preceded by the value under test). + virtual std::string describe() const { + std::ostringstream ss; + ss << "is between " << m_begin << " and " << m_end; + return ss.str(); + } +}; + +// The builder function +inline IntRange IsBetween( int begin, int end ) { + return IntRange( begin, end ); +} + +// ... + +// Usage +TEST_CASE("Integers are within a range") +{ + CHECK_THAT( 3, IsBetween( 1, 10 ) ); + CHECK_THAT( 100, IsBetween( 1, 10 ) ); +} +``` + +Running this test gives the following in the console: + +``` +/**/TestFile.cpp:123: FAILED: + CHECK_THAT( 100, IsBetween( 1, 10 ) ) +with expansion: + 100 is between 1 and 10 +``` + +--- + +[Home](Readme.md) \ No newline at end of file