【问题标题】:Python Requests Mock doesn't catch Timeout exceptionPython Requests Mock 没有捕捉到超时异常
【发布时间】:2016-03-19 00:22:48
【问题描述】:

我写了一个单元测试来测试请求包的超时

my_module.py:

import requests

class MyException(Exception): pass

def my_method():
    try:
        r = requests.get(...)
    except requests.exceptions.Timeout:
        raise MyException()

单元测试:

from mock import patch
from unittest import TestCase
from requests.exceptions import Timeout

from my_module import MyException

@patch('my_module.requests')
class MyUnitTest(TestCase):
    def my_test(self, requests):
        def get(*args, **kwargs):
            raise Timeout()

        requests.get = get

        try:
            my_module.my_method(...)
        except MyException:
            return

        self.fail("No Timeout)

但是当它运行时,my_method 中的 try 块永远不会捕获 requests.exceptions.Timeout

【问题讨论】:

    标签: python python-requests python-mock


    【解决方案1】:

    我在这里看到了两个问题。一个可以直接解决您的问题,第二个是对 Mocking 框架的轻微误用,进一步简化了您的实现。

    首先,根据您希望如何测试您的断言以及您在此处实际想要做什么来直接解决您的问题:

    requests.get = get
    

    应该在此处使用side_effect 来帮助提出您的异常。根据documentation

    side_effect 允许您执行副作用,包括提高 调用模拟时的异常

    考虑到这一点,您真正需要做的就是:

    requests.get.side_effect = get
    

    这应该会引发您的异常。但是,您可能会遇到此错误:

    TypeError: catching classes that do not inherit from BaseException is not allowed
    

    这可以最好通过实际阅读 this 很好的答案来解释为什么会发生这种情况。有了这个答案,采用该建议实际上只模拟出您需要的内容将有助于完全解决您的问题。所以,最后,你的代码实际上看起来像这样,使用模拟的 get 而不是模拟的 requests 模块:

    class MyUnitTest(unittest.TestCase):
    
        @patch('my_module.requests.get')
        def test_my_test(self, m_get):
            def get(*args, **kwargs):
                raise Timeout()
    
            m_get.side_effect = get
    
            try:
                my_method()
            except MyException:
                return
    

    您现在实际上可以通过更好地使用 assertRaises 而不是 try/except 的单元测试中的内容来进一步简化此操作。这最终只会断言在调用方法时引发了异常。此外,您需要创建会引发超时的新方法,实际上您可以简单地声明您的模拟 get 将具有引发异常的 side_effect。所以你可以简单地用这个替换整个def get

    m_get.side_effect = Timeout()
    

    但是,您实际上可以直接将其放入您的补丁装饰器中,所以,现在您的最终代码将如下所示:

    class MyUnitTest(unittest.TestCase):
    
        @patch('my_module.requests.get', side_effect=Timeout())
        def test_my_test(self, m_get):    
            with self.assertRaises(MyException):
                my_method()
    

    我希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      patch('my_module.requests') 将用新的模拟对象替换 my_module.requests,但是在您的测试方法中,您替换了直接导入的 requests.get 方法,因此在原始请求模块中,这意味着更改不会反映在您的模块。

      如果在您的测试方法中将其替换为 my_module 中的 requests 模拟,它应该可以工作:

      my_module.requests.get = get
      

      【讨论】:

        猜你喜欢
        • 2016-02-28
        • 1970-01-01
        • 1970-01-01
        • 2018-01-03
        • 1970-01-01
        • 1970-01-01
        • 2011-11-17
        • 1970-01-01
        • 2023-04-04
        相关资源
        最近更新 更多