Python 3.x Python单元测试失败,因为来自模拟的StopIteration错误
我正在使用mock测试一个类对象,它是ccxt.binance。这个模拟对象作为参数传递给我正在测试的函数。最初它是有效的,因为我能够在一定程度上进行测试。但是,当我更改所述对象的值时,函数不考虑该值,它返回StopIteration错误。我是不是错过了一些模拟设置?下面是代码的一个示例片段 下面是正在测试的代码Python 3.x Python单元测试失败,因为来自模拟的StopIteration错误,python-3.x,ccxt,Python 3.x,Ccxt,我正在使用mock测试一个类对象,它是ccxt.binance。这个模拟对象作为参数传递给我正在测试的函数。最初它是有效的,因为我能够在一定程度上进行测试。但是,当我更改所述对象的值时,函数不考虑该值,它返回StopIteration错误。我是不是错过了一些模拟设置?下面是代码的一个示例片段 下面是正在测试的代码 def-exchange\u-history\u加载(args、ccxt\u-exchange): #如果没有真正的fetchOHLCV或exchange没有特定于时间段的数据,则跳过
def-exchange\u-history\u加载(args、ccxt\u-exchange):
#如果没有真正的fetchOHLCV或exchange没有特定于时间段的数据,则跳过exchange
如果((不是ccxt_exchange.has['fetchOHLCV'])或(ccxt_exchange.has['fetchOHLCV']=='emulated')或
(不是ccxt_exchange.timeframes.get(args.timeframe)):
logging.warning(f'Skipping{ccxt_exchange.name}.)
返回
#检查数据库中是否有exchange。如果没有,则写入数据库。
exchange\u db\u data=get\u exchange(ccxt\u exchange.id)
如果不交换数据库数据:
在数据库中插入交换(ccxt\u exchange.id,ccxt\u exchange.name)
logging.info(f'Inserted{ccxt_exchange.name}到数据库.)
exchange\u db\u data=get\u exchange(ccxt\u exchange.id)
exchange\u db\u id=exchange\u db\u数据[0]
info(f'Fetched{ccxt_exchange.name}数据)
#负荷交换市场
市场=ccxt_交易所。加载_市场()
info(f'Loaded{ccxt_exchange.name}markets.)
返回交换数据库id,市场
下面是测试函数
@patch('src.cron.historical\u price.hp\u load\u data.insert\u exchange\u to\u db')
@补丁('src.cron.historical\u price.hp\u load\u data.get\u exchange')
@修补程序('ccxt.binance')
def测试交换历史加载(模拟二进制、模拟获取交换、模拟插入交换到数据库):
#为函数创建示例参数
args=argparse.Namespace(timeframe='1d')
#必要函数的模拟示例返回值
mock_get_exchange.side_effect=[无,(1,'二进制','二进制',(1,'二进制','二进制')]
mock_binance.return_value.has.return_value={'fetchOHLCV':True}
mock\u binance.return\u value.load\u markets.return\u value={
“BTC/USDT”:{
'id':'btcusdt',
“符号”:“BTC/USDT”,
“基本”:“BTC”,
‘报价’:‘USDT’,
“活动”:True
}
}
mock_binance.return_value.id.return_value='binance'
mock_binance.return_value.name.return_value='binance'
ccxt_exchange=ccxt.binance()
#案例1:exchange具有fetchOHLCV,timeframe,但不在数据库中
#使用案例1测试断言
交易所\数据库\ id,市场=交易所\历史\加载(args,ccxt\交易所)
断言类型(交换数据库id)==int
断言类型(市场)=dict
断言mock\u get\u exchange.call\u count==2
calls=[调用(mock\u binance.return\u value.id),调用(mock\u binance.return\u value.id)]
模拟\u获取\u交换。断言\u有\u调用(调用)
mock_insert_exchange_to_db.assert_调用_once()
mock_binance.return_value.load_markets.assert_called_once()
#案例2:exchange具有fetchOHLCV、时间表,并且在数据库中
#重置案例2的模拟调用
mock\u插入\u交换\u到\u db.reset\u mock()
mock\u get\u exchange.reset\u mock()
mock\u binance.return\u value.load\u markets.reset\u mock()
#使用案例2测试断言
交易所\数据库\ id,市场=交易所\历史\加载(args,ccxt\交易所)
断言类型(交换数据库id)==int
断言类型(市场)=dict
mock_get_exchange.assert_调用_once()
assert not mock_insert_exchange_to_db.called
mock_binance.return_value.load_markets.assert_called_once()
calls=[调用(mock\u binance.return\u value.id)]
模拟\u获取\u交换。断言\u有\u调用(调用)
#案例3:exchange没有真正的fetchOHLCV
#重置案例3的模拟调用
mock\u get\u exchange.reset\u mock()
mock\u插入\u交换\u到\u db.reset\u mock()
mock\u binance.reset\u mock()
mock_binance.return_value.has.return_value={'fetchOHLCV':True}
#mock\u binance.return\u value.load\u markets.reset\u mock()
#使用案例3测试断言
交易所\数据库\ id,市场=交易所\历史\加载(args,ccxt\交易所)
#断言不交换\u db\u id
#不要断言市场
assert not mock_get_exchange.called
assert not mock_insert_exchange_to_db.called
assert not mock\u binance.return\u value.load\u markets.called
案例1和案例2有效。但是对于案例3,会抛出一个错误
mock_binance = <MagicMock name='binance' id='140285596974272'>
mock_get_exchange = <MagicMock name='get_exchange' id='140285596584312'>
mock_insert_exchange_to_db = <MagicMock name='insert_exchange_to_db' id='140285596584480'>
@patch('src.cron.historical_price.hp_load_data.insert_exchange_to_db')
@patch('src.cron.historical_price.hp_load_data.get_exchange')
@patch('ccxt.binance')
def test_exchange_history_load(mock_binance, mock_get_exchange, mock_insert_exchange_to_db):
# create sample arguments for function
args = argparse.Namespace(timeframe='1d')
# mock up sample return values for necessary functions
mock_get_exchange.side_effect = [None, (1, 'binance', 'Binance'), (1, 'binance', 'Binance')]
mock_binance.return_value.has.return_value = {'fetchOHLCV': True}
mock_binance.return_value.load_markets.return_value = {
'BTC/USDT': {
'id': 'btcusdt',
'symbol': 'BTC/USDT',
'base': 'BTC',
'quote': 'USDT',
'active': True
}
}
mock_binance.return_value.id.return_value = 'binance'
mock_binance.return_value.name.return_value = 'Binance'
ccxt_exchange = ccxt.binance()
# case 1: exchange is not in database
# test assertions with case 1
exchange_db_id, markets = exchange_history_load(args, ccxt_exchange)
assert type(exchange_db_id) == int
assert type(markets) == dict
assert mock_get_exchange.call_count == 2
calls = [call(mock_binance.return_value.id), call(mock_binance.return_value.id)]
mock_get_exchange.assert_has_calls(calls)
mock_insert_exchange_to_db.assert_called_once()
mock_binance.return_value.load_markets.assert_called_once()
# case 2: exchange is in database
# reset mock calls for case 2
mock_insert_exchange_to_db.reset_mock()
mock_get_exchange.reset_mock()
mock_binance.return_value.load_markets.reset_mock()
# test assertions with case 2
exchange_db_id, markets = exchange_history_load(args, ccxt_exchange)
assert type(exchange_db_id) == int
assert type(markets) == dict
mock_get_exchange.assert_called_once()
assert not mock_insert_exchange_to_db.called
mock_binance.return_value.load_markets.assert_called_once()
calls = [call(mock_binance.return_value.id)]
mock_get_exchange.assert_has_calls(calls)
# case 3: exchange doesn't have true fetchOHLCV
# reset_mock calls for case 3
mock_get_exchange.reset_mock()
mock_insert_exchange_to_db.reset_mock()
mock_binance.reset_mock()
mock_binance.return_value.has.return_value = {'fetchOHLCV': True}
# mock_binance.return_value.load_markets.reset_mock()
# test assertions with case 3
> exchange_db_id, markets = exchange_history_load(args, ccxt_exchange)
test_cron/test_historical_price/test_hp_load_data.py:89:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../src/cron/historical_price/hp_load_data.py:75: in exchange_history_load
exchange_db_data = get_exchange(ccxt_exchange.id)
/usr/lib/python3.6/unittest/mock.py:939: in __call__
return _mock_self._mock_call(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_mock_self = <MagicMock name='get_exchange' id='140285596584312'>
args = (<MagicMock name='binance().id' id='140285596659328'>,), kwargs = {}
self = <MagicMock name='get_exchange' id='140285596584312'>, _new_name = ''
_new_parent = None
_call = call(<MagicMock name='binance().id' id='140285596659328'>), seen = set()
skip_next_dot = False, do_method_calls = False, name = 'get_exchange'
def _mock_call(_mock_self, *args, **kwargs):
self = _mock_self
self.called = True
self.call_count += 1
_new_name = self._mock_new_name
_new_parent = self._mock_new_parent
_call = _Call((args, kwargs), two=True)
self.call_args = _call
self.call_args_list.append(_call)
self.mock_calls.append(_Call(('', args, kwargs)))
seen = set()
skip_next_dot = _new_name == '()'
do_method_calls = self._mock_parent is not None
name = self._mock_name
while _new_parent is not None:
this_mock_call = _Call((_new_name, args, kwargs))
if _new_parent._mock_new_name:
dot = '.'
if skip_next_dot:
dot = ''
skip_next_dot = False
if _new_parent._mock_new_name == '()':
skip_next_dot = True
_new_name = _new_parent._mock_new_name + dot + _new_name
if do_method_calls:
if _new_name == name:
this_method_call = this_mock_call
else:
this_method_call = _Call((name, args, kwargs))
_new_parent.method_calls.append(this_method_call)
do_method_calls = _new_parent._mock_parent is not None
if do_method_calls:
name = _new_parent._mock_name + '.' + name
_new_parent.mock_calls.append(this_mock_call)
_new_parent = _new_parent._mock_new_parent
# use ids here so as not to call __hash__ on the mocks
_new_parent_id = id(_new_parent)
if _new_parent_id in seen:
break
seen.add(_new_parent_id)
ret_val = DEFAULT
effect = self.side_effect
if effect is not None:
if _is_exception(effect):
raise effect
if not _callable(effect):
> result = next(effect)
E StopIteration
/usr/lib/python3.6/unittest/mock.py:998: StopIteration
mock\u binance=
模拟获取交换=
模拟插入交换到数据库=
@修补程序('src.cron.historical\u price.hp\u load\u data.insert\u exchange\u to\u db')
@补丁('src.cron.historical\u price.hp\u load\u data.get\u exchange')
@修补程序('ccxt.binance')
def测试交换历史加载(模拟二进制、模拟获取交换、模拟插入交换到数据库):
#为函数创建示例参数
args=argparse.Namespace(timeframe='1d')
#必要函数的模拟示例返回值
mock_get_exchange.side_effect=[无,(1,'二进制','二进制',(1,'二进制','二进制')]
mock_binance.return_value.has.return_value={'fetchOHLCV':True}
mock\u binance.return\u value.load\u markets.return\u value={
“BTC/USDT”:{
'id':'btcusdt',
“符号”:“BTC/USDT”,
“基本”:“BTC”,
‘报价’:‘USDT’,
“活动”:True
}
}
mock_binance.return_value.id.return_value='binance'
mock_binance.return_value.name.return_value='binance'
ccxt_exchange=ccxt.binance()
#情况1:exchange不在数据库中
#使用案例1测试断言
交易所\数据库\ id,市场=交易所\历史\加载(args,ccxt\交易所)
断言类型(交换数据库id)==int
断言类型(市场)=dict
断言mock\u get\u exchange.call\u count==2
calls=[调用(mock\u binance.return\u value.id),调用(mock\u binance.return\u value.id)]
mock\u get\u exchange.assert\u has\u调用
In [1]: from fleeteng_fw_agent.hostinfo imfrom mock import MagicMock,
PropertyMock, call, patch
In [2]: mock_get_exchange = MagicMock()
In [3]: mock_get_exchange.side_effect = [None, (1, 'binance', 'Binance'), (1,
'binance', 'Binance')]
In [4]: mock_get_exchange
Out[4]: <MagicMock id='4310663120'>
In [5]: mock_get_exchange()
In [6]: mock_get_exchange()
Out[6]: (1, 'binance', 'Binance')
In [7]: mock_get_exchange()
Out[7]: (1, 'binance', 'Binance')
In [8]: mock_get_exchange()
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-8-4d63b6292e10> in <module>()
----> 1 mock_get_exchange()
/Users/**/ in __call__(_mock_self, *args, **kwargs)
954 # in the signature
955 _mock_self._mock_check_sig(*args, **kwargs)
--> 956 return _mock_self._mock_call(*args, **kwargs)
957
958
/Users/**/ in _mock_call(_mock_self, *args, **kwargs)
1012
1013 if not _callable(effect):
-> 1014 result = next(effect)
1015 if _is_exception(result):
1016 raise result
StopIteration: