Python请求Mock不会捕获超时异常

时间:2016-03-19 00:22:49

标签: python python-requests python-mock

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

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

2 个答案:

答案 0 :(得分:5)

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

首先,根据您希望测试断言的方式,直接解决您的问题,您实际上在这里要做的是:

requests.get = get

在这里使用side_effect来帮助提高你的例外。根据{{​​3}}:

  

side_effect允许你执行副作用,包括提高   调用mock时的异常

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

requests.get.side_effect = get

这应该让你的例外加注。但是,您可能可能面临此错误:

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

这可以通过实际阅读documentation关于为什么会发生这种情况的好答案来解释 。有了这个答案,采取这个建议实际上只是模拟你需要将有助于完全解决你的问题。所以,最后,您的代码实际上看起来像这样,使用模拟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更好地利用unittest中的内容来进一步简化此操作。这最终只会声明在调用方法时引发异常。此外,您不需要 来创建一个会引发超时的新方法,您实际上可以简单地声明您的模拟获取将具有引发异常的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()

我希望这有帮助!

答案 1 :(得分:0)

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

如果在您的测试方法中将其替换为my_module中的请求模拟,则应该有效:

my_module.requests.get = get