["python","unit-testing","mocking"]
unittest.mock: asserting partial match for method argument

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