【问题标题】:Adding an OverflowError side effect with patch - Python使用补丁添加 OverflowError 副作用 - Python
【发布时间】:2018-04-13 12:40:39
【问题描述】:

我想模拟一个OverflowError,因为我想在引发异常之后测试一个变量的值。但是,我不知道如何使用我正在使用的库复制溢出错误。我在这个特定测试中使用的库是pysolar.solar,特别是get_altitudeget_azimuthradiation methods

在盲目地尝试不同的数字来模拟溢出错误后,我决定尝试模拟函数并引入副作用。

我正在测试的代码 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 仍然没有通过我的断言?

谢谢

更新测试通过

根据@Gang 的建议,OverFlowError 被转载。我意识到,为了测试块的异常,特别是 radiationnp.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])

【问题讨论】:

    标签: python unit-testing exception mocking patch


    【解决方案1】:

    为什么我引入的第一个 OverflowError 仍然没有通过我的断言?

    差不多了。 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)
    

    查看文档,是如何在assertRaises里面调用一个func

    assertRaises(异常,可调用,*args,**kwds)

    fun(*args, **kwds) 提高 exc

    这个用法是错误的:

    self.assertRaises(OverflowError,  sunposition.sun_position(lat=error_lat, lon=error_lon, time=error_time))
    

    它应该是一个带有kwargs的func名称:

    self.assertRaises(OverflowError,  sunposition.sun_position, lat=error_lat, lon=error_lon, time=error_time)
    

    【讨论】:

    • 太棒了!我错误地断言它@Gang。关于我修补的位置,我意识到这模拟了OverFlowError,而不是我想要的,因为最后我真正想要的是检查radiationnp.nan。但首先我必须确保产生了溢出错误。谢谢回答我的问题。我正在为我想要测试的内容更新上述内容(使用正确的补丁)(以防它帮助任何人)。
    猜你喜欢
    • 2021-04-04
    • 1970-01-01
    • 2021-02-22
    • 2015-06-18
    • 1970-01-01
    • 1970-01-01
    • 2011-08-31
    • 1970-01-01
    • 2019-11-11
    相关资源
    最近更新 更多