Python 模拟可迭代(某种程度上)不起作用

Python Mock Iterable (sort of) not working

我正在为 Flask 应用程序创建一个测试模块。在这个 Resource 的 get() 方法中,我从 Mongo 获取数据,然后遍历它以生成输出。我正在将集合的 find() 方法模拟为 return 我的可迭代对象。问题是在 get() 中我循环遍历可迭代对象的地方,它跳过了,就好像它是空的一样。所以我尝试在测试中循环遍历可迭代对象,我可以成功地看到它应该包含的 3 个字典。

Class 属性:

class _TestAll(BaseAllReports):
    collection = MagicMock()
    bool_columns = ('bool1', 'bool2')
    string_set_columns = ('string1', 'string2')
    int_columns = ('int1', 'int2')
    text_columns = ('text1', 'text2')
    stats_columns = ('bool1', 'int1')

Resource.get():

def get(self):
    args = self.parser().parse_args()
    search = self.search_doc(args)

    docs = self.collection.find(search, {'_id': False})
    print(docs)

    ids, total_hurt, total_dead = set(), 0, 0
    stats = dict((x, {}) for x in self.stats_columns)
    stats['month'] = {}
    for d in docs:
        print('get', d)
        if d['id'] in ids:
            continue
        else:
            ids.add(d['id'])
        for s in self.stats_columns:
            if s in self.bool_columns:
                key = u'Sí' if d[s] else 'No'
            else:
                key = d[s]
            number = stats[s].get(key, 0) + 1
            stats[s][key] = number

        month_key = d['timestamp'].strftime('%b')
        month_summary = stats['month'].get(month_key, {'hurt': 0, 'dead': 0})
        month_summary['hurt'] += d['total_hurt']
        month_summary['dead'] += d['total_dead']
        stats['month'][month_key] = month_summary

        total_hurt += d['total_hurt']
        total_dead += d['total_dead']
    return {
        'incidents': len(ids),
        'involved': docs.count(),
        'affected': total_hurt + total_dead,
        'hurt': total_hurt,
        'dead': total_dead,
        'stats': stats
    }

测试设置:

@classmethod
def setUpClass(cls):
    app.testing = True
    cls.app = app.test_client()
    cls.url = '/incidents'
    cls.url_with_key = '/incidents?key=testKeyHash'
    api.add_resource(_TestAll, cls.url)

测试:

def test_get(self):
    with patch('__main__._TestAll.collection.find') as search:
        answer = []
        for i in range(3):
            answer.append({
                'id': i,
                'bool1': True, 'bool2': False,
                'string1': 'test', 'string2': 'test',
                'int1': 1, 'int2': 2,
                'text1': 'test', 'text2': 'test',
                'timestamp': datetime.now(), 'total_hurt': 1, 'total_dead': 0})
        search.__iter__.return_value = answer
        search.return_value.count.return_value = len(answer)
        response = self.app.get(self.url_with_key)
        data = json.loads(response.data.decode())
        for i in search:
            print('test', i)
        print(data)
        self.assertEqual(_TestAll.collection.find.call_count, 1)
        self.assertIn('stats', data)
        for s in _TestAll.stats_columns:
            self.assertIn(s, data['stats'])

终端输出:

<MagicMock name='find()' id='4423760080'>
('test', {'timestamp': datetime.datetime(2017, 5, 25, 13, 3, 9, 255912), 'text2': 'test', 'text1': 'test', 'int1': 1, 'int2': 2, 'id': 0, 'bool1': True, 'bool2': False, 'total_hurt': 1, 'total_dead': 0, 'string2': 'test', 'string1': 'test'})
('test', {'timestamp': datetime.datetime(2017, 5, 25, 13, 3, 9, 255923), 'text2': 'test', 'text1': 'test', 'int1': 1, 'int2': 2, 'id': 1, 'bool1': True, 'bool2': False, 'total_hurt': 1, 'total_dead': 0, 'string2': 'test', 'string1': 'test'})
('test', {'timestamp': datetime.datetime(2017, 5, 25, 13, 3, 9, 255928), 'text2': 'test', 'text1': 'test', 'int1': 1, 'int2': 2, 'id': 2, 'bool1': True, 'bool2': False, 'total_hurt': 1, 'total_dead': 0, 'string2': 'test', 'string1': 'test'})
{u'stats': {u'bool1': {}, u'int1': {}, u'month': {}}, u'involved': 3, u'dead': 0, u'hurt': 0, u'incidents': 0, u'affected': 0}

我不明白为什么资源不能正确地循环遍历可迭代对象,但测试不能。感谢任何帮助。

谢谢

设置__iter__值时,该行是

search.__iter__.return_value = answer

我没有考虑到 filter() 是可调用的。实现我所尝试的正确方法是:

search.return_value.__iter__.return_value = answer

因为调用了search Mock,返回了一个新的MagicMock,显然没有设置__iter__ 属性。资源的 get() 和测试函数正在访问不同的模拟,这就是为什么它只对其中一个起作用的原因。

我发现的方法是在测试方法中打印模拟并为其获取不同的模拟 ID。