Matchers¶
lemoncheesecake comes with support of matchers, a feature inspired by Hamcrest / PyHamcrest.
The matchers¶
The following stock matchers are available:
Values:
equal_to(expected)
: check if actual==
expectednot_equal_to(expected)
: check if actual!=
expectedgreater_than(expected)
: check if actual>
expectedgreater_than_or_equal_to(expected)
: check if actual>=
expectedless_than(expected)
: check if actual<
expectedless_than_or_equal_to(expected)
: check if actual<=
expectedis_between(min, max)
: check if actual is between min and maxis_none()
: check if actual== None
is_not_none()
: check if actual!= None
has_length(expected)
: check if value has expected length (expected
can be a value or aMatcher
object)is_true()
: check if value is a boolean trueis_false()
: check if value is a boolean falseis_json(expected)
: check is the actual JSON equalsexpected
, if not, the unified diff of actual vs expected is displayed
Character strings:
starts_with(expected)
: check if the actual string starts withexpected
ends_with(expected)
: check if the actual string ends withexpected
contains_string(expected)
: check if the actual containsexpected
match_pattern(expected)
: check if the actual string matchexpected
regexp (expected can be a raw string or an object returned byre.compile()
)is_text(expected)
: check is the actual (multi-lined) text equalsexpected
, if not, the unified diff of actual vs expected is displayed
Types (
expected
is optional and can be a value or a matcher object):is_integer([expected])
: check if actual is of typeint
is_float([expected])
: check if actual is of typefloat
is_bool([expected])
: check if actual is of typebool
is_str([expected])
: check if actual is of typestr
is_list([expected])
: check if actual is of typelist
ortuple
is_dict([expected])
: check if actual is of typedict
Iterable:
has_item(expected)
: check if the actual iterable has an item that matches expected (expected can be a value or a Matcher)has_items(expected)
: check if the actual iterable contains at least the expected items (raw values)has_only_items(expected)
: check if the actual iterable only contains the expected items (raw values)has_all_items(expected)
: check if all items of the actual iterable match the expected matcheris_in(expected)
: check if actual is among the expected items
Dict:
has_entry(expected_key [,expected_value])
: check if actual dict hasexpected_key
and (optionally) the expected associated valueexpected_value
(which can be a value or a matcher)
Logical:
is_(expected)
: return the matcher ifexpected
is a matcher, otherwise wrapsexpected
in theequal_to
matcheris_not(expected)
,not_(expected)
: make the negation of theexpected
matcher (orequal_to
if the argument is not a matcher)all_of(matcher1, [matcher2, [...]])
: check if all the matchers succeed (logical AND between all the matchers)any_of(matcher1, [matcher2, [...]])
: check if any of the matchers succeed (logical OR between all the matchers)anything()
,something()
,existing()
,present()
: these matchers always succeed whatever the actual value is (only the matcher description changes to fit the matcher’s name)
The matching operations¶
Those matcher are used by a matching function:
check_that(hint, actual, matcher, quiet=False)
: run the matcher, log the result and return the matching result as a booleanrequire_that(hint, actual, matcher, quiet=False)
: run the matcher, log the result and raise anAbortTest
exception in case of a match failureassert_that(hint, actual, matcher, quiet=False)
: run the match, in case of a match failure (and only in this case) log the result and raise anAbortTest
exception
The quiet
flag can be set to True
to hide the matching result details in the report.
The lemoncheesecake.matching
module also provides helper functions to ease operations on nested data:
The code:
data = {"foo": 1, "bar": 2}
check_that("data", data, has_entry("foo", equal_to(1)))
check_that("data", data, has_entry("bar", equal_to(2)))
Can be shortened like this:
check_that_in(
{"foo": 1, "bar": 2},
"foo", equal_to(1),
"bar", equal_to(2)
)
Nested dicts can be checked easily by expressing the nested keys as a tuple
:
check_that_in(
{"foo": {"bar": 1}},
("foo", "bar"), equal_to(1),
)
Nested lists are also supported:
check_that_in(
{"foo": [{"bar": 1}]},
("foo", 0, "bar"), equal_to(1),
)
The base_key
keyword-argument can also be used when checking nested dicts:
check_that_in(
{"foo": {"bar": 1, "baz": 2}},
"bar", equal_to(1),
"baz", equal_to(2),
base_key=("foo",)
)
The same dict helper counterparts are available for:
require_that
=>require_that_in
assert_that
=>assert_that_in
Like their *_that
counterpart, the *_that_in
functions can also take a quiet
keyword argument.
If one match fails in a test, this test will be marked as failed.
New in version 1.11.0.
The *_that_in
functions have been improved to make nested data checking easier and more powerful.
This:
check_that_in(
{"foo": {"bar": 1, "baz": 2},
("foo", "bar"), equal_to(1),
("foo", "baz"), equal_to(2)
)
Can now be written as this:
check_that_in(
{"foo": {"bar": 1, "baz": 2},
{"foo": {"bar": equal_to(1), "baz": equal_to(2)}
)
It leads to the exact same result, with two distinct equal_to
matching operations.
It also means that in addition of the even number (key/matcher) arguments for the “expected” argument, these functions now also accept a single argument form.
Creating custom matchers¶
A custom matcher example:
from lemoncheesecake.matching.matcher import Matcher, MatchResult
class MultipleOf(Matcher):
def __init__(self, value):
self.value = value
def build_description(self, transformation):
return transformation("to be a multiple of %s" % self.value)
def matches(self, actual):
return MatchResult(actual % self.value == 0, "got %s" % actual)
def multiple_of(value):
return MultipleOf(value)
And how to use it:
check_that("value", 42, is_(multiple_of(2))
A matcher must inherit the Matcher
class and implements two methods:
build_description
and matches
.
the
build_description
method will build the description part of the matcher in the check description using the instance ofMatcherDescriptionTransformer
passed as argument. This callable will do a transformation of the description such as conjugating the verb or turn it into its negative form depending on the calling context. The former example will produce this description for instance:Expect value to be a multiple of 2
.Here are two examples of transformations depending on the context:
check_that("value", 42, is_(not_(multiple_of(2))) # => "Expect value to not be a multiple of 2" check_that("value", 42, is_integer(multiple_of(2))) # => "Expect value to be an integer that is a multiple of 2"
the
matches
method tests if passed argument fulfills the matcher requirements. The method must return an instance ofMatchResult
that will indicate whether or not the match succeed and an optional match description.
New in version 1.11.0.
The description and the result details from an existing matcher instance can be changed using the methods
Matcher.override_description
and Matcher.hide_result_details
:
check_that("value", 1, equal_to(1).override_description("to be one"))
check_that("value", 1, equal_to(1).hide_result_details())