Skip to content

Commit

Permalink
docs(matchers): add section on custom matchers (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
mcous authored Jul 6, 2023
1 parent 9ea6fad commit 0e13a0c
Showing 1 changed file with 45 additions and 0 deletions.
45 changes: 45 additions & 0 deletions docs/usage/matchers.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,48 @@ def test_event_listener(decoy: Decoy):
This is a pretty verbose way of writing a test, so in general, you may want to approach using `matchers.Captor` as a form of potential code smell / test pain. There are often better ways to structure your code for these sorts of interactions that don't involve private functions.

For further reading on when (or rather, when not) to use argument captors, check out [testdouble's documentation on its argument captor matcher](https://github.com/testdouble/testdouble.js/blob/main/docs/6-verifying-invocations.md#tdmatcherscaptor).

## Writing custom matchers

You can write your own matcher class and use it wherever you would use a built-in matcher. All you need to do is define a class with an `__eq__` method:

```python
class Is42:
def __eq__(self, other: object) -> bool:
return other == 42

check_answer = decoy.mock(name="check_answer")

decoy.when(
check_answer(Is42())
).then_return("huzzah!")

assert check_answer(42) == "huzzah!"
assert check_answer(43) is None
```

This is especially useful if the value objects you are using as arguments are difficult to compare and out of your control. For example, Pandas [DataFrame][] objects do not return a `bool` from `__eq__`, which makes it difficult to compare calls.

We can define a `MatchesDataFrame` class to work around this:

```python
import pandas as pd

class MatchesDataFrame:
def __init__(self, data) -> None:
self._data_frame = pd.DataFrame(data)

def __eq__(self, other: object) -> bool:
return self._data_frame.equals(other)

check_data = decoy.mock(name="check_data")

decoy.when(
check_answer(MatchesDataFrame({"x1": range(1, 42)}))
).then_return("huzzah!")

assert check_data(pd.DataFrame({"x1": range(1, 42)})) == "huzzah!"
assert check_data(pd.DataFrame({"x1": range(1, 43)})) is None
```

[DataFrame]: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html

0 comments on commit 0e13a0c

Please sign in to comment.