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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With