如何在python / Mock中模拟用于for循环的查询集

时间:2014-02-12 15:45:31

标签: python django unit-testing mocking

我正在编写一些单元测试,并希望使用Mock。

给出以下代码:

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

跑步时,我得到了

  

TypeError:'模拟'对象不可迭代

我尝试过修补

@mock.patch('__builtin__.iter')

但我似乎无法让它发挥作用。 我还没有成功地弄清楚当for循环查询设置“使用”时真正发生了什么。

非常感谢帮助!

[使用进一步添加的示例代码编辑,在第一个解决方案提案后]

3 个答案:

答案 0 :(得分:2)

您必须使用已定义__iter__的{​​{3}}和iterator

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

答案 1 :(得分:1)

通常我将QuerySet模拟为一个列表,这似乎更容易。所以:

something.return_value = [item]

其中something是您计算QuerySet的函数或位置。作为一个实际的例子:

MyModel.objects.filter.return_value = [item]

这仅适用于您未使用QuerySet特定特征的情况。

答案 2 :(得分:1)

我的一位同事帮我解决了这个问题。我想要以下代码。

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