使用补丁添加 OverflowError 副作用 - Python
Adding an OverflowError side effect with patch - Python
我想模拟一个 OverflowError
因为我想在引发异常后测试变量的值。但是,我不知道如何使用我正在使用的库复制 OverflowError。我在此特定测试中使用的库是 pysolar.solar
,特别是 get_altitude
、get_azimuth
和 radiation methods
在盲目地尝试不同的数字来模拟 OverflowError 之后,我决定尝试模拟函数并引入副作用。
我正在测试的代码 sunposition.py
import numpy as np
import pandas as pd
from pysolar.solar import get_altitude, get_azimuth, radiation as radiation_module
def sun_position(lat: float, lon: float, time: pd.Timestamp = None) -> List[float]:
if time is None:
time = pd.Timestamp.now(tz='UTC')
dt = time.to_pydatetime()
altitude = get_altitude(lat, lon, dt)
azimuth = get_azimuth(lat, lon, dt)
try:
radiation = radiation_module.get_radiation_direct(dt, altitude)
except OverflowError:
radiation = np.nan
return pd.Series([altitude, azimuth, radiation], index=['Alt', 'Azi', 'Rad'])
**我开始用补丁做什么**
"""Test sunposition module"""
import unittest
import numpy as np
import pandas as pd
from unittest.mock import MagicMock, patch, Mock
from bigfolder.sun import sunposition
class TestSunposition(unittest.TestCase):
"""Test functions in sunposition."""
def test_sun_position_overflow_error(self):
error_lat = 23
error_lon = 12
error_time = pd.Timestamp('2007-02-18 15:13:05', tz="UTC")
mock_args = {'side_effect': OverflowError}
with patch('bigfolder.sun.sunposition.sun_position', **mock_args):
# run the test
self.assertRaises(OverflowError, sunposition.sun_position(lat=error_lat, lon=error_lon, time=error_time))
if __name__ == '__main__':
unittest.main()
我期待它给我和 OverFlow 错误...它确实发生了,但是我的断言无论如何都失败了 OverflowError 我的猜测是我在错误的地方打了补丁?我真的不明白为什么无论何时错误仍然是 OverFlow Error
测试都会失败
这是打印出来的
_mock_self = <MagicMock name='sun_position' id='102333856'>, args = ()
kwargs = {'lat': 23, 'lon': 12, 'time': Timestamp('2007-02-18 15:13:05+0000', tz='UTC')}
self = <MagicMock name='sun_position' id='102333856'>, _new_name = ''
_new_parent = None
_call = call(lat=23, lon=12, time=Timestamp('2007-02-18 15:13:05+0000', tz='UTC'))
seen = set(), skip_next_dot = False, do_method_calls = False
name = 'sun_position'
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
E OverflowError
所以我想我一定是在错误的地方打了补丁并且比我应该的更早引入了副作用?所以我改为在 try 块中修补该方法。 这是我的后续代码。
def test_sun_position_overflow_error(self):
error_lat = 23
error_lon = 12
error_time = pd.Timestamp('2007-02-18 15:13:05', tz="UTC")
mock_args = {'side_effect': OverflowError}
with patch('bigfolder.sun.sunposition.sun_position.radiation_module.get_radiation_direct', **mock_args):
# run the test
self.assertRaises(OverflowError, sunposition.sun_position(lat=error_lat, lon=error_lon, time=error_time))
现在我的错误是“ModuleNotFoundError: No module named 'bigfolder.sun.sunposition.sun_position'; 'bigfolder.sun.sunposition' is not a package
”
然后我将路径更改为 'sun_position.radiation_module.get_radiation_direct'
,但没有找到模块。
所以我的问题是:如何复制 OverflowError 以便我可以在引发异常时检查我设置的变量的值。为什么我介绍的第一个OverflowError还是没有通过我的断言?
谢谢
更新测试通过
按照@Gang 的建议,转载了OverFlowError
。我意识到,为了测试块的异常,特别是 radiation
当时是 np.nan
我必须修补 我想要 的方法以获得 OverFlowError
而不是 sun_position 的整个方法。当我尝试这样做时,我错误地导入了它,因为我认为外部库是代码的一部分。所以我将 bigfolder.sun.sunposition.sun_position.radiation_module.get_radiation_direct
更改为 pysolar.solar.radiation.get_radiation_direct
这是具有我想要模拟的 get_radiation_direct 方法的外部库。
def test_sun_position_overflow_error(self):
lat = 23
lon = 12
time = pd.Timestamp('2007-02-18 15:13:05', tz="UTC")
# get_radiation_direct will now produce an OverFlowError(regardless of coordinates)
mock_args = {'side_effect': OverflowError}
# mock get_radiation_direct and produce OverFlowError
with patch('pysolar.solar.radiation.get_radiation_direct', **mock_args):
# Check radiation column is nan value
assert math.isnan(sunposition.sun_position(lat=lat, lon=lon, time=time)[2])
Why is the first OverflowError I introduce still not pass my assertion?
快到了。 assertRaises
的正确方法:
def test_sun_position_overflow_error(self):
# This has to be here first and without func call
with self.assertRaises(OverflowError):
# patch the function to have an exception no matter what
mock_args = {'side_effect': OverflowError}
with patch('bigfolder.sun.sunposition.sun_position', **mock_args):
# call this func to trigger an exception
sunposition.sun_position(lat=error_lat, lon=error_lon, time=error_time)
看了文档,里面是怎么调用一个func的assertRaises
assertRaises(exception, callable, *args, **kwds)
fun(*args, **kwds) raises exc
这种用法是错误的:
self.assertRaises(OverflowError, sunposition.sun_position(lat=error_lat, lon=error_lon, time=error_time))
它应该是一个带有 kwargs 的函数名称:
self.assertRaises(OverflowError, sunposition.sun_position, lat=error_lat, lon=error_lon, time=error_time)
我想模拟一个 OverflowError
因为我想在引发异常后测试变量的值。但是,我不知道如何使用我正在使用的库复制 OverflowError。我在此特定测试中使用的库是 pysolar.solar
,特别是 get_altitude
、get_azimuth
和 radiation methods
在盲目地尝试不同的数字来模拟 OverflowError 之后,我决定尝试模拟函数并引入副作用。
我正在测试的代码 sunposition.py
import numpy as np
import pandas as pd
from pysolar.solar import get_altitude, get_azimuth, radiation as radiation_module
def sun_position(lat: float, lon: float, time: pd.Timestamp = None) -> List[float]:
if time is None:
time = pd.Timestamp.now(tz='UTC')
dt = time.to_pydatetime()
altitude = get_altitude(lat, lon, dt)
azimuth = get_azimuth(lat, lon, dt)
try:
radiation = radiation_module.get_radiation_direct(dt, altitude)
except OverflowError:
radiation = np.nan
return pd.Series([altitude, azimuth, radiation], index=['Alt', 'Azi', 'Rad'])
**我开始用补丁做什么**
"""Test sunposition module"""
import unittest
import numpy as np
import pandas as pd
from unittest.mock import MagicMock, patch, Mock
from bigfolder.sun import sunposition
class TestSunposition(unittest.TestCase):
"""Test functions in sunposition."""
def test_sun_position_overflow_error(self):
error_lat = 23
error_lon = 12
error_time = pd.Timestamp('2007-02-18 15:13:05', tz="UTC")
mock_args = {'side_effect': OverflowError}
with patch('bigfolder.sun.sunposition.sun_position', **mock_args):
# run the test
self.assertRaises(OverflowError, sunposition.sun_position(lat=error_lat, lon=error_lon, time=error_time))
if __name__ == '__main__':
unittest.main()
我期待它给我和 OverFlow 错误...它确实发生了,但是我的断言无论如何都失败了 OverflowError 我的猜测是我在错误的地方打了补丁?我真的不明白为什么无论何时错误仍然是 OverFlow Error
这是打印出来的
_mock_self = <MagicMock name='sun_position' id='102333856'>, args = ()
kwargs = {'lat': 23, 'lon': 12, 'time': Timestamp('2007-02-18 15:13:05+0000', tz='UTC')}
self = <MagicMock name='sun_position' id='102333856'>, _new_name = ''
_new_parent = None
_call = call(lat=23, lon=12, time=Timestamp('2007-02-18 15:13:05+0000', tz='UTC'))
seen = set(), skip_next_dot = False, do_method_calls = False
name = 'sun_position'
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
E OverflowError
所以我想我一定是在错误的地方打了补丁并且比我应该的更早引入了副作用?所以我改为在 try 块中修补该方法。 这是我的后续代码。
def test_sun_position_overflow_error(self):
error_lat = 23
error_lon = 12
error_time = pd.Timestamp('2007-02-18 15:13:05', tz="UTC")
mock_args = {'side_effect': OverflowError}
with patch('bigfolder.sun.sunposition.sun_position.radiation_module.get_radiation_direct', **mock_args):
# run the test
self.assertRaises(OverflowError, sunposition.sun_position(lat=error_lat, lon=error_lon, time=error_time))
现在我的错误是“ModuleNotFoundError: No module named 'bigfolder.sun.sunposition.sun_position'; 'bigfolder.sun.sunposition' is not a package
”
然后我将路径更改为 'sun_position.radiation_module.get_radiation_direct'
,但没有找到模块。
所以我的问题是:如何复制 OverflowError 以便我可以在引发异常时检查我设置的变量的值。为什么我介绍的第一个OverflowError还是没有通过我的断言?
谢谢
更新测试通过
按照@Gang 的建议,转载了OverFlowError
。我意识到,为了测试块的异常,特别是 radiation
当时是 np.nan
我必须修补 我想要 的方法以获得 OverFlowError
而不是 sun_position 的整个方法。当我尝试这样做时,我错误地导入了它,因为我认为外部库是代码的一部分。所以我将 bigfolder.sun.sunposition.sun_position.radiation_module.get_radiation_direct
更改为 pysolar.solar.radiation.get_radiation_direct
这是具有我想要模拟的 get_radiation_direct 方法的外部库。
def test_sun_position_overflow_error(self):
lat = 23
lon = 12
time = pd.Timestamp('2007-02-18 15:13:05', tz="UTC")
# get_radiation_direct will now produce an OverFlowError(regardless of coordinates)
mock_args = {'side_effect': OverflowError}
# mock get_radiation_direct and produce OverFlowError
with patch('pysolar.solar.radiation.get_radiation_direct', **mock_args):
# Check radiation column is nan value
assert math.isnan(sunposition.sun_position(lat=lat, lon=lon, time=time)[2])
Why is the first OverflowError I introduce still not pass my assertion?
快到了。 assertRaises
的正确方法:
def test_sun_position_overflow_error(self):
# This has to be here first and without func call
with self.assertRaises(OverflowError):
# patch the function to have an exception no matter what
mock_args = {'side_effect': OverflowError}
with patch('bigfolder.sun.sunposition.sun_position', **mock_args):
# call this func to trigger an exception
sunposition.sun_position(lat=error_lat, lon=error_lon, time=error_time)
看了文档,里面是怎么调用一个func的assertRaises
assertRaises(exception, callable, *args, **kwds)
fun(*args, **kwds) raises exc
这种用法是错误的:
self.assertRaises(OverflowError, sunposition.sun_position(lat=error_lat, lon=error_lon, time=error_time))
它应该是一个带有 kwargs 的函数名称:
self.assertRaises(OverflowError, sunposition.sun_position, lat=error_lat, lon=error_lon, time=error_time)