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== Noneis_not_none(): check if actual!= Nonehas_length(expected): check if value has expected length (expectedcan be a value or aMatcherobject)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 withexpectedends_with(expected): check if the actual string ends withexpectedcontains_string(expected): check if the actual containsexpectedmatch_pattern(expected): check if the actual string matchexpectedregexp (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 (
expectedis optional and can be a value or a matcher object):is_integer([expected]): check if actual is of typeintis_float([expected]): check if actual is of typefloatis_bool([expected]): check if actual is of typeboolis_str([expected]): check if actual is of typestris_list([expected]): check if actual is of typelistortupleis_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_keyand (optionally) the expected associated valueexpected_value(which can be a value or a matcher)
Logical:
is_(expected): return the matcher ifexpectedis a matcher, otherwise wrapsexpectedin theequal_tomatcheris_not(expected),not_(expected): make the negation of theexpectedmatcher (orequal_toif 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 anAbortTestexception 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 anAbortTestexception
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_inassert_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_descriptionmethod will build the description part of the matcher in the check description using the instance ofMatcherDescriptionTransformerpassed 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
matchesmethod tests if passed argument fulfills the matcher requirements. The method must return an instance ofMatchResultthat 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())