unittest.mock: asserting partial match for method argument
Solution 1:
import mock
class AnyStringWith(str):
def __eq__(self, other):
return self in other
...
result = database.Query('complicated sql with an id: %s' % id)
database.Query.assert_called_once_with(AnyStringWith(id))
...
Preemptively requires a matching string
def arg_should_contain(x):
def wrapper(arg):
assert str(x) in arg, "'%s' does not contain '%s'" % (arg, x)
return wrapper
...
database.Query = arg_should_contain(id)
result = database.Query('complicated sql with an id: %s' % id)
UPDATE
Using libraries like callee
, you don't need to implement AnyStringWith
.
from callee import Contains
database.Query.assert_called_once_with(Contains(id))
https://callee.readthedocs.io/en/latest/reference/operators.html#callee.operators.Contains
Solution 2:
You can just use unittest.mock.ANY
:)
from unittest.mock import Mock, ANY
def foo(some_string):
print(some_string)
foo = Mock()
foo("bla")
foo.assert_called_with(ANY)
As described here - https://docs.python.org/3/library/unittest.mock.html#any
Solution 3:
You can use match_equality
from PyHamcrest library to wrap the matches_regexp
matcher from the same library:
from hamcrest.library.integration import match_equality
with patch(database) as MockDatabase:
instance = MockDatabase.return_value
...
expected_arg = matches_regexp(id)
instance.Query.assert_called_once_with(match_equality(expected_arg))
This method is mentioned also in Python's unittest.mock
documentation:
As of version 1.5, the Python testing library PyHamcrest provides similar functionality, that may be useful here, in the form of its equality matcher (hamcrest.library.integration.match_equality).
If you don't want to use PyHamcrest, the documentation linked above also shows how to write a custom matcher by defining a class with an __eq__
method (as suggested in falsetru
s answer):
class Matcher:
def __init__(self, compare, expected):
self.compare = compare
self.expected = expected
def __eq__(self, actual):
return self.compare(self.expected, actual)
match_foo = Matcher(compare, Foo(1, 2))
mock.assert_called_with(match_foo)
You could replace the call to self.compare
here with your own regex matching and return False
if none found or raise an AssertionError
with a descriptive error message of your choice.