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]
To mock a method in a class with @patch. object but return a different value each time it is called, use side_effect. Side effect allows you to define a custom method and have that method called each time your mock method is called. The value returned from this method will be used as the return value your mock method.
So what is the difference between them? MagicMock is a subclass of Mock . It contains all magic methods pre-created and ready to use (e.g. __str__ , __len__ , etc.). Therefore, you should use MagicMock when you need magic methods, and Mock if you don't need them.
A QuerySet is a collection of data from a database. A QuerySet is built up as a list of objects. QuerySets makes it easier to get the data you actually need, by allowing you to filter and order the data. In this tutorial we will be querying data from the Members table.
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()
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.
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()
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