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



我正在编写一些单元测试,想使用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()

运行时,我得到

类型错误:"模拟"对象不可迭代

我试过打补丁

@mock.patch('__builtin__.iter')

但我似乎无法让它工作。我还没有成功地弄清楚当查询集被 for 循环"使用"时到底发生了什么。

非常感谢帮助!

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

你必须

使用iteratorMagicMock__iter__定义

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

通常我嘲笑QuerySet是一个列表,这似乎更容易。所以:

something.return_value = [item]

其中something是计算查询集的函数或位置。举个实际例子:

MyModel.objects.filter.return_value = [item]

这仅在不使用 QuerySet 特定特征时才有效。

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

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

最新更新