如何在python中模拟导入的pypi库使用的函数调用

Posted

技术标签:

【中文标题】如何在python中模拟导入的pypi库使用的函数调用【英文标题】:how to mock function call used by imported pypi library in python 【发布时间】:2016-01-26 13:33:08 【问题描述】:

我正在尝试测试以下代码:

great_report.py

from retry import retry

@retry((ReportNotReadyException), tries=3, delay=10, backoff=3)
def get_link(self):
    report_link = _get_report_link_from_3rd_party(params)
    if report_link:
        return report_link
    else:
        stats.count("report_not_ready", 1)
        raise ReportNotReadyException

我有我的测试函数,它模拟 _get_report_link_from_3rd_party 测试所有内容,但我不希望这个函数在我运行测试期间实际暂停执行..

@mock.patch('repo.great_report._get_report_link_from_3rd_party', return_value=None)
test_get_link_raises_exception(self, mock_get_report_link):
    self.assertRaises(ReportNotReadyException, get_link)

我尝试模拟重试参数,但遇到了 get_link 不断重试的问题,这会导致构建时间过长,而不仅仅是引发异常并继续。如何在我的测试中模拟 @retry 调用的参数?

【问题讨论】:

【参考方案1】:

正如here 所暗示的,防止实际睡眠的一种简单方法是修补time.sleep 函数。这是为我做的代码:

@patch('time.sleep', side_effect = lambda _: None)

【讨论】:

【参考方案2】:

加载模块后无法更改装饰器参数。装饰器装饰原始功能并在模块加载时对其进行更改。

首先,我想鼓励您对设计稍作改动,使其更具可测试性。

如果您提取get_link() 方法的主体测试新方法并信任retry 装饰器,您将获得您的目标。

如果您不想为您的类添加新方法,您可以使用一个配置模块来存储您在调用retry 装饰器时使用的变量。之后,您可以使用两个不同的模块进行测试和生产。

最后一种方式是黑客方式,您将 retry.api.__retry_internal 替换为您的版本,该版本仅通过更改变量来调用原始版本:

import unittest
from unittest.mock import *
from pd import get_link, ReportNotReadyException

import retry
orig_retry_internal = retry.api.__retry_internal
def _force_retry_params(new_tries=-1, new_delay=0, new_max_delay=None, new_backoff=1, new_jitter=0):
    def my_retry_internals(f, exceptions, tries, delay, max_delay, backoff, jitter, logger):
        # call original __retry_internal by new parameters
        return orig_retry_internal(f, exceptions, tries=new_tries, delay=new_delay, max_delay=new_max_delay,
                                   backoff=new_backoff, jitter=new_jitter, logger=logger)
    return my_retry_internals


class MyTestCase(unittest.TestCase):
    @patch("retry.api.__retry_internal", side_effect=_force_retry_params(new_tries=1))
    def test_something(self, m_retry):
        self.assertRaises(ReportNotReadyException, get_link, None)

恕我直言,您应该仅在遇到困难并且没有机会重新设计代码以使其更具可测试性时才使用该黑客解决方案。 内部函数/类/方法可能会更改,恕不另行通知,您的测试将来可能难以维护

【讨论】:

以上是关于如何在python中模拟导入的pypi库使用的函数调用的主要内容,如果未能解决你的问题,请参考以下文章

python导入库

如何在绝对导入中设置没有点符号的 Pypi 包 - python3

python使用国内镜像库

如何在脚本中列出与导入对应的 PyPI 包的名称?

如何在Pypi上发表自己的Python库

python中pil如何导入?