Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 244
lambshaanxy Avatar asked Jun 07 '13 04:06

lambshaanxy


1 Answers

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

like image 126
falsetru Avatar answered Sep 27 '22 01:09

falsetru