断言模拟调用__iter __()方法

时间:2013-04-19 13:50:45

标签: python unit-testing mocking

from mock import MagicMock, call
m = MagicMock()
m.foo()
for i in m:
    print m
m.bar()
print m.mock_calls
[call.foo(), call.__iter__(), call.bar()]

[call.foo(), call.__iter__(), call.bar()] == m.mock_calls
False

如何断言在一系列调用中迭代了一个模拟对象?如果我将__iter__.return_value设置为其他内容,也会发生同样的事情。

4 个答案:

答案 0 :(得分:2)

一个有效但难看的解决方案是[call.foo(), ('__iter__', (), {}), call.bar()] == m.mock_calls

答案 1 :(得分:1)

潜在的问题是mock.call返回mock._Call的实例,而tuple本身是__iter__的子类。作为一个元组,它继承了元组的('__iter__', (), {})方法,并且您正在获取该实际方法,而不是像往常一样动态捕获属性访问以产生链式调用。

可以说,比在呼叫清单中对call.__getattr__('__iter__')()进行硬编码更好的一种可能的解决方法是改为>>> mock.call.__getattr__('__iter__')() == ('__iter__', (), {}) True ,其效果大致相同:

getattr(mock.call, '__iter__')

(请注意,您无法执行tuple.__iter__,因为这只会再次为您提供实际的mock._Call方法)

或者,您可以继承__iter__的子类,并自己构建call的行为较好的版本(至少相对于class _IterCall(mock._Call): def __getattr__(self, attr): if self._mock_name is None: return type(self)(name=attr, from_kall=False) name = '%s.%s' % (self._mock_name, attr) return type(self)(name=name, parent=self, from_kall=False) def __iter__(self): return self.__getattr__('__iter__')() iter_call = _IterCall(from_kall=True) 而言):

__getattr__

(它也必须覆盖mock._Call.__getattr__,因为mock._Call的实现将显式地返回原始type(self)类的实例,而不是>>> iter_call.__iter__() == ('__iter__', (), {}) True

import requests
import pandas as pd


url = 'https://stats.nba.com/stats/leaguedashplayerstats'

payload = {
'College': '',
'Conference': '',
'Country': '',
'DateFrom': '',
'DateTo': '',
'Division': '',
'DraftPick': '',
'DraftYear': '',
'GameScope': '',
'GameSegment': '',
'Height': '',
'LastNGames': '0',
'LeagueID': '00',
'Location': '',
'MeasureType': 'Base',
'Month': '0',
'OpponentTeamID': '0',
'Outcome': '',
'PORound': '0',
'PaceAdjust': 'N',
'PerMode': 'PerGame',
'Period': '0',
'PlayerExperience':'' ,
'PlayerPosition': '',
'PlusMinus': 'N',
'Rank': 'N',
'Season': '2019-20',
'SeasonSegment': '',
'SeasonType': 'Regular Season',
'ShotClockRange': '',
'StarterBench': '',
'TeamID': '0',
'TwoWay': '0',
'VsConference': '',
'VsDivision':'' ,
'Weight': ''}

jsonData = requests.get(url, params=payload).json()


cols = jsonData['resultSets'][0]['headers']
rows = jsonData['resultSets'][0]['rowSet']

df = pd.DataFrame(rows, columns=cols)

两种方法都有缺点。

答案 2 :(得分:0)

如果调用了模拟对象,则其called属性为True

>>> m = MagicMock()
>>> assert m.__iter__.called, "The object was not iterated over"
Traceback (most recent call last):
  File "<string>", line 1, in <fragment>
builtins.AssertionError: The object was not iterated over
>>> for i in m:
...     print(m)
... 
>>> assert m.__iter__.called, "The object was not iterated over"
>>> m.__iter__.called
True

在这种情况下,m.__iter__也是一个模拟,它是“按需”创建的(作为访问__iter__方法的结果)。

答案 3 :(得分:0)

我尝试避免猜测正确的语法,因此我写了helper library来根据实际调用为我生成断言。

如果在代码末尾添加以下行:

import mock_autogen.generator
print(mock_autogen.generator.generate_asserts(m))

您会得到:

m.foo.assert_called_once_with()
m.__iter__.assert_called_once_with()
m.bar.assert_called_once_with()

因此,您不再需要自己找出确切的语法,只需应用该工具即可:)