--- tags: [Python Unit Tests at Hypothesis] redirect_from: - /post/matchers/ --- Matcher Objects in Python Tests =============================== This post covers one final technique that's used in the Hypothesis tests: **matcher objects**. Matcher objects are a way to make complex assertions easier to read and write, make the intents of tests clearer, and reduce repetition between test methods. If you find yourself writing complex assertions that obscure the intent of your test, and especially if you find yourself repeating similar complex assertions in multiple tests, consider using a matcher. Example ------- [This test for `GroupCreateController()`](https://github.com/hypothesis/h/blob/c480243e112e2cd5f54b7b65e40a6891ca57fbfe/tests/h/views/groups_test.py#L24) tests that posting the create new group form returns a redirect: ```python def test_post_redirects_if_form_valid(controller): result = controller.post() assert isinstance(result, httpexceptions.HTTPSeeOther) assert result.location == '/g/abc123/fake-group' ``` This and every other test that wants to assert that something returns a particular kind of redirect (302, 303...) needs to repeat two assertions: that the correct type of Pyramid redirect exception is returned and that the exception has the correct `location` value. This test can be simplified by using the [`redirect_303_to` matcher](https://github.com/hypothesis/h/blob/c480243e112e2cd5f54b7b65e40a6891ca57fbfe/tests/common/matchers.py#L108): ```python def test_post_redirects_if_form_valid(controller, matchers): assert controller.post() == matchers.redirect_303_to('/g/abc123/fake-group') ``` This assertion will only pass if `controller.post()` returns an `HTTPSeeOther` with the correct `location`. A three-line test becomes one line, even with this very simple matcher you can save a lot of lines of test code when this pattern is repeated in every test that needs to check for a redirect. How matcher objects work ------------------------ Matcher classes are simply classes that implement [Python's `__eq__()` magic method](https://docs.python.org/2/reference/datamodel.html#object.__eq__). Normally an object instance of a Python class isn't equal to any other object except itself - `==` comparisons will always evaluate to `False`: ```python >>> class Foo(object): ... pass ... >>> foo_1 = Foo() >>> # The object is equal to itself: >>> foo_1 == foo_1 True >>> # But the object isn't equal to any other object: >>> foo_2 = Foo() >>> foo_1 == foo_2 False >>> foo_1 == 23 False >>> foo_1 == "anything" False ``` But you can customize the behavior of `==` by providing an `__eq__()` method in your class: ```python >>> class Foo(object): ... def __eq__(self, other): ... return self.location == other.location ... >>> foo_1 = Foo() >>> foo_1.location = "/example" >>> # The object is equal to any other object that has the same `location`: >>> foo_2 = Foo() >>> foo_2.location = "/example" >>> foo_1 == foo_2 True >>> # Other comparison operators such as `in` also work: >>> foo_1 in [foo_2, 23, "bar"] >>> # But the object is _not_ equal to objects with a different `location`: >>> foo_3 = Foo() >>> foo_3.location = "/different" >>> foo_1 == foo_3 False ``` Whenever it comes across a `==` expression Python calls the `__eq__()` method of the object on the left hand side of the expression. The `__eq__()` method returns `True` or `False` and this becomes the result of the `==` expression. Other standard comparison operators such as `in` also work with `__eq__()`. A matcher class is simply a class with an `__eq__()` method, here's the `redirect_303_to` matcher that we used above: ```python class redirect_303_to(Matcher): """Matches any HTTPSeeOther redirect to the given URL.""" def __init__(self, location): self.location = location def __eq__(self, other): if not isinstance(other, httpexceptions.HTTPSeeOther): return False return other.location == self.location ``` So when you create a `redirect_303_to` object with `matchers.redirect_303_to('/g/abc123/fake-group')`, in `==` comparisons that matcher object will be equal to any `HTTPSeeOther` object with the same location, and so the matcher can be used in assertions: ```python assert controller.post() == matchers.redirect_303_to('/g/abc123/fake-group') ``` The [`Matcher` class](https://github.com/hypothesis/h/blob/c480243e112e2cd5f54b7b65e40a6891ca57fbfe/tests/common/matchers.py#L35) that `redirect_303_to` inherits from simply provides an implementation of [the `__ne__()` method](https://docs.python.org/2/reference/datamodel.html#object.__ne__), which Python calls to evaluate `!=` and `<>` expressions. Any class that implements `__eq__()` should also implement `__ne__()`, by inheriting from `Matcher` matcher classes don't have to implement `__ne__()` themselves. Where to find the available matcher classes ------------------------------------------- To use a matcher a test method has a parameter named `matchers`:

def test_post_redirects_if_form_valid(controller, matchers):
    assert controller.post() == matchers.redirect_303_to('/g/abc123/fake-group')
`matchers` is a [pytest fixture]({{ site.baseurl }}{% post_url 2017-02-02-fixtures %}) that contains all of the matcher classes [defined in matchers.py](https://github.com/hypothesis/h/blob/c480243e112e2cd5f54b7b65e40a6891ca57fbfe/tests/common/matchers.py) as attributes (it's the `matchers.py` module, made available as a fixture). Of course a matcher class is just a class with an `__eq__()` method and a test module can just contain its own matcher classes, but if a matcher class seems like it might be useful across multiple test modules then consider putting it in `matchers.py` to make it available via the `matchers` fixture. Another example --------------- `==` comparisons and `__eq__()` are used under-the-hood in a lot of places in Python. For example the [mock library]({{ site.baseurl }}{% post_url 2017-03-17-mock %})'s `assert_called_once_with()` method uses it to test whether the argument that the mock method was called with matches the test's expected argument, so you can pass a mock object as the expected argument. Matchers really come into their own when used in this way: ```python def test_it_fetches_the_groups_from_the_database(_fetch_groups, group_pubids, matchers): execute() _fetch_groups.assert_called_once_with(matchers.unordered_list(group_pubids)) ``` Without the `unordered_list` matcher the test would have had to `assert` that `_fetch_groups` was called exactly once, that it was called with exactly one argument, and then implement [a 6-line algorithm to check that the argument contained exactly those values in `group_pubids` but regardless of order](https://github.com/hypothesis/h/blob/c480243e112e2cd5f54b7b65e40a6891ca57fbfe/tests/common/matchers.py#L146) (using sets would not implement this correctly). With a matcher the test can express this all in one line. Matcher objects and value objects --------------------------------- I mentioned **value objects** in my post about [When and When Not to Use Mocks]({{ site.baseurl }}{% post_url 2017-04-25-when-to-use-mocks %}). Value objects are simple, immutable objects that implement `__eq__()` and `__ne__()` and that are used in production code (not just in tests). Code can be designed to enable isolated unit testing without mocks by using value objects that are so simple that they don't need to be mocked. [The mock library]({{ site.baseurl }}{% post_url 2017-03-17-mock %})'s [`mock.call`](http://www.voidspace.org.uk/python/mock/helpers.html#call) is an example of a value object - two `call` objects with the same values are equal: ```python >>> import mock >>> call_1 = mock.call(1, 2, 3) >>> call_2 = mock.call(1, 2, 3) >>> call_1 == call_2 True ``` Every time you call a mock object it adds a `call` object to its `call_args_list`: ```python >>> my_mock = mock.MagicMock() >>> my_mock("foo") >>> my_mock.call_args_list [call('foo')] ``` Because `call` objects are value objects you can create your own `call` objects and compare them to the ones in a mock's `call_args_list` using standard Python comparison operators like `in` and `==`: ```python >>> mock.call("foo") in my_mock.call_args_list True >>> mock.call("bar") in my_mock.call_args_list False >>> mock.call("foo") == my_mock.call_args_list[0] True >>> my_mock.call_args_list == [mock.call("foo")] True ``` This can be useful when making complex assertions about how a mock was called, especially when multiple calls are involved and their order is important. If your code uses value objects then your tests may not need to use matcher objects as the tests can just use the value object classes directly. Your tests may still need matcher objects if they want to do some alternative comparison instead of the one implemented by the value object's `__eq__()` method (comparisons like the unordered list comparison above), or if the code under test doesn't use value objects.