Reputation: 81
I am writing some unit tests and want to use Mock.
Given the following code:
# the 'real' query set is a Django database model
# qs = SomeDjangoModel.objects.filter(name='some_name')
qs = mock.Mock()
qs.filter.return_value = qs
item = mock.Mock()
item.do_work.return_value = "Some text"
qs.iter.return_value = iter([item])
# below is the code I want to test..
qs = qs.filter(name='some name')
qs = qs.filter(valid_from__lte=Timezone.now())
for obj in qs:
obj.do_work()
when run, I get
TypeError: 'Mock' object is not iterable
I have tried patching
@mock.patch('__builtin__.iter')
but I just can't seem to get it to work. I haven't succeeded in figuring out what really goes on when the query set "used" by the for-loop.
Help is greatly appreciated!
[edited with further added example code, after first solution proposal]
Upvotes: 7
Views: 6665
Reputation: 859
You have to use iterator
and MagicMock
which has __iter__
defined
from unittest.mock import Mock, MagicMock
from datetime import datetime
qs = MagicMock()
qs.filter.return_value = qs
item = Mock()
item.do_work.return_value = "Some text"
qs.iterator.return_value = iter([item])
# below is the code I want to test..
qs = qs.filter(name='some name')
qs = qs.filter(valid_from__lte=datetime.now())
for obj in qs:
obj.do_work()
Upvotes: 3
Reputation: 81
A colleague of mine helped me solve this. The following code does I wanted.
def the_iter(self):
return iter(self.my_test_list)
def test_my_code(self):
qs = mock.Mock()
qs.filter.return_value = qs
the_item = mock.Mock()
the_item.do_work.return_value = "Some text"
self.my_test_list = [the_item]
qs.__iter__ = mock.Mock(side_effect=self.the_iter)
# below is the code I want to test..
qs = qs.filter(name='some name')
qs = qs.filter(colour='Yellow')
for obj in qs:
print obj.do_work()
Upvotes: 1
Reputation: 122376
Usually I mock the QuerySet to be a list instead, that seems easier. So:
something.return_value = [item]
where something
is the function or place where you're computing the QuerySet. As an actual example:
MyModel.objects.filter.return_value = [item]
This only works if you're not using QuerySet specific characteristics.
Upvotes: 1