Rubyist writing Python here. I've got some code that looks kinda like this:
result = database.Query('complicated sql with an id: %s' % id)
database.Query
is mocked out, and I want to test that the ID gets injected in correctly without hardcoding the entire SQL statement into my test. In Ruby/RR, I would have done this:
mock(database).query(/#{id}/)
But I can't see a way to set up a 'selective mock' like that in unittest.mock, at least without some hairy side_effect
logic. So I tried using the regexp in the assertion instead:
with patch(database) as MockDatabase:
instance = MockDatabase.return_value
...
instance.Query.assert_called_once_with(re.compile("%s" % id))
But that doesn't work either. This approach does work, but it's ugly:
with patch(database) as MockDatabase:
instance = MockDatabase.return_value
...
self.assertIn(id, instance.Query.call_args[0][0])
Better ideas?
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