# 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++ using Catch::Matchers::EndsWith; // or Catch::EndsWith 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" ) ) ); ``` _The combining operators do not take ownership of the matcher objects. This means that if you store the combined object, you have to ensure that the matcher objects outlive its last use. What this means is that code like this leads to a use-after-free and (hopefully) a crash:_ ```cpp TEST_CASE("Bugs, bugs, bugs", "[Bug]"){ std::string str = "Bugs as a service"; auto match_expression = Catch::EndsWith( "as a service" ) || (Catch::StartsWith( "Big data" ) && !Catch::Contains( "web scale" ) ); REQUIRE_THAT(str, match_expression); } ``` ## Built in matchers Catch2 provides some matchers by default. They can be found in the `Catch::Matchers::foo` namespace and are imported into the `Catch` namespace as well. There are two parts to each of the built-in matchers, the matcher type itself and a helper function that provides template argument deduction when creating templated matchers. As an example, the matcher for checking that two instances of `std::vector` are identical is `EqualsMatcher`, but the user is expected to use the `Equals` helper function instead. ### String matchers The string matchers are `StartsWith`, `EndsWith`, `Contains`, `Equals` and `Matches`. The first four match a literal (sub)string against a result, while `Matches` takes and matches an ECMAScript regex. Do note that `Matches` matches the string as a whole, meaning that "abc" will not match against "abcd", but "abc.*" will. Each of the provided `std::string` matchers also takes an optional second argument, that decides case sensitivity (by-default, they are case sensitive). ### Vector matchers Catch2 currently provides 5 built-in matchers that work on `std::vector`. These are * `Contains` which checks whether a specified vector is present in the result * `VectorContains` which checks whether a specified element is present in the result * `Equals` which checks whether the result is exactly equal (order matters) to a specific vector * `UnorderedEquals` which checks whether the result is equal to a specific vector under a permutation * `Approx` which checks whether the result is "approx-equal" (order matters, but comparison is done via `Approx`) to a specific vector > Approx matcher was [introduced](https://github.com/catchorg/Catch2/issues/1499) in Catch 2.7.2. ### Floating point matchers Catch2 provides 3 matchers for working with floating point numbers. These are `WithinAbsMatcher`, `WithinUlpsMatcher` and `WithinRelMatcher`. The `WithinAbsMatcher` matcher accepts floating point numbers that are within a certain distance of target. It should be constructed with the `WithinAbs(double target, double margin)` helper. The `WithinUlpsMatcher` matcher accepts floating point numbers that are within a certain number of [ULPs](https://en.wikipedia.org/wiki/Unit_in_the_last_place) of the target. Because ULP comparisons need to be done differently for `float`s and for `double`s, there are two overloads of the helpers for this matcher, `WithinULP(float target, int64_t ULPs)`, and `WithinULP(double target, int64_t ULPs)`. The `WithinRelMatcher` matcher accepts floating point numbers that are _approximately equal_ with the target number with some specific tolerance. In other words, it checks that `|lhs - rhs| <= epsilon * max(|lhs|, |rhs|)`, with special casing for `INFINITY` and `NaN`. There are _4_ overloads of the helpers for this matcher, `WithinRel(double target, double margin)`, `WithinRel(float target, float margin)`, `WithinRel(double target)`, and `WithinRel(float target)`. The latter two provide a default epsilon of machine epsilon * 100. > `WithinRel` matcher was introduced in Catch 2.10.0 ### Generic matchers Catch also aims to provide a set of generic matchers. Currently this set contains only a matcher that takes arbitrary callable predicate and applies it onto the provided object. Because of type inference limitations, the argument type of the predicate has to be provided explicitly. Example: ```cpp REQUIRE_THAT("Hello olleH", Predicate( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") ); ``` The second argument is an optional description of the predicate, and is used only during reporting of the result. ### Exception matchers Catch2 also provides an exception matcher that can be used to verify that an exception's message exactly matches desired string. The matcher is `ExceptionMessageMatcher`, and we also provide a helper function `Message`. The matched exception must publicly derive from `std::exception` and the message matching is done _exactly_, including case. > `ExceptionMessageMatcher` was introduced in Catch 2.10.0 Example use: ```cpp REQUIRE_THROWS_MATCHES(throwsDerivedException(), DerivedException, Message("DerivedException::what")); ``` ## 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 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 override { 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#top)