Python Requests Mock не улавливает исключение тайм-аута

Я написал модульный тест для проверки тайм-аута с пакетом запросов.

мой_модуль.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)

Но когда он запускается, блок try в my_method никогда не перехватывает requests.exceptions.Timeout


person Hank    schedule 19.03.2016    source источник


Ответы (2)


Я вижу здесь две проблемы. Один из них напрямую решает вашу проблему, а второй представляет собой небольшое неправильное использование фреймворка Mocking, что еще больше упрощает вашу реализацию.

Во-первых, чтобы напрямую решить вашу проблему, основываясь на том, как вы хотите проверить свое утверждение, что вы на самом деле хотите сделать здесь:

requests.get = get

Вы должны использовать здесь side_effect, чтобы вызвать исключение. Согласно документации:

side_effect позволяет выполнять побочные эффекты, в том числе вызывать исключение при вызове макета

Имея это в виду, все, что вам действительно нужно сделать, это:

requests.get.side_effect = get

Это должно заставить ваше исключение подняться. Однако есть вероятность, что вы можете столкнуться с этой ошибкой:

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

Это можно лучше объяснить, прочитав этот отличный ответ о том, почему это происходит. Если вы примете этот ответ, приняв это предложение, чтобы на самом деле только издеваться над тем, что вам нужно, это поможет полностью решить вашу проблему. Итак, в конце концов, ваш код будет выглядеть примерно так, с имитируемым модулем 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

Теперь вы можете еще больше упростить это, лучше используя то, что есть в unittest, с помощью 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()

Надеюсь, это поможет!

person idjaw    schedule 19.03.2016

patch('my_module.requests') заменит my_module.requests новым фиктивным объектом, но в вашем тестовом методе вы замените метод requests.get непосредственно импортированного и, следовательно, исходного модуля запросов, что означает, что изменение не отражается в вашем модуле.

Это должно работать, если в вашем тестовом методе вы вместо этого замените его на запросы, имитирующие ваш my_module:

my_module.requests.get = get
person mata    schedule 19.03.2016