简体   繁体   中英

How to mock a queryset for use in a for loop in python/Mock

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]

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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM