【问题标题】:Mocking a function to raise an Exception to test an except block模拟函数以引发异常以测试除块
【发布时间】:2015-04-03 00:46:18
【问题描述】:

我有一个函数 (foo),它调用另一个函数 (bar)。如果调用bar()引发HttpError,如果状态码为404我想特别处理,否则重新引发。

我正在尝试围绕这个foo 函数编写一些单元测试,模拟对bar() 的调用。不幸的是,我无法获得对bar() 的模拟调用来引发我的except 块捕获的异常。

这是说明我的问题的代码:

import unittest
import mock
from apiclient.errors import HttpError


class FooTests(unittest.TestCase):
    @mock.patch('my_tests.bar')
    def test_foo_shouldReturnResultOfBar_whenBarSucceeds(self, barMock):
        barMock.return_value = True
        result = foo()
        self.assertTrue(result)  # passes

    @mock.patch('my_tests.bar')
    def test_foo_shouldReturnNone_whenBarRaiseHttpError404(self, barMock):
        barMock.side_effect = HttpError(mock.Mock(return_value={'status': 404}), 'not found')
        result = foo()
        self.assertIsNone(result)  # fails, test raises HttpError

    @mock.patch('my_tests.bar')
    def test_foo_shouldRaiseHttpError_whenBarRaiseHttpErrorNot404(self, barMock):
        barMock.side_effect = HttpError(mock.Mock(return_value={'status': 500}), 'error')
        with self.assertRaises(HttpError):  # passes
            foo()

def foo():
    try:
        result = bar()
        return result
    except HttpError as error:
        if error.resp.status == 404:
            print '404 - %s' % error.message
            return None
        raise

def bar():
    raise NotImplementedError()

我遵循了Mock docs,它说您应该将Mock 实例的side_effect 设置为Exception 类,以使模拟函数引发错误。

我还查看了一些其他相关的 StackOverflow 问答,看起来我正在做与他们正在做的相同的事情,以导致他们的模拟引发异常。

为什么设置barMockside_effect 不会导致预期的Exception 被提升?如果我在做一些奇怪的事情,我应该如何在 except 块中测试逻辑?

【问题讨论】:

  • 我很确定您的异常 is 正在引发,但我不确定您如何在那里设置 resp.status 代码。 HTTPError 来自哪里?
  • @MartijnPieters HttpError 是我们在 GAE 中使用的 Google's apiclient lib 中定义的类。它的__init__ 是用参数(resp, content) 定义的,所以我试图为响应创建一个模拟实例,并指定适当的状态代码。
  • 对,原来是this class;但是您不需要用户return_valueresp 未被调用
  • 我在没有使用HttpError 的情况下再次尝试了我的代码,而是尝试使用常规的Exception 实例。这完美地工作。这意味着它必须与我如何配置HttpError 实例有关,可能与我如何为响应创建Mock 实例有关。

标签: python unit-testing python-2.7 mocking python-mock


【解决方案1】:

您的模拟可以很好地引发异常,但是缺少 error.resp.status 值。与其使用return_value,不如告诉Mock status 是一个属性:

barMock.side_effect = HttpError(mock.Mock(status=404), 'not found')

Mock() 的其他关键字参数被设置为结果对象的属性。

我将您的 foobar 定义放在 my_tests 模块中,添加到 HttpError class 中,这样我也可以使用它,然后您的测试就可以成功运行:

>>> from my_tests import foo, HttpError
>>> import mock
>>> with mock.patch('my_tests.bar') as barMock:
...     barMock.side_effect = HttpError(mock.Mock(status=404), 'not found')
...     result = my_test.foo()
... 
404 - 
>>> result is None
True

您甚至可以看到print '404 - %s' % error.message 行运行,但我认为您想在那里使用error.content;无论如何,这就是第二个参数设置的属性HttpError()

【讨论】:

  • side_effect 是关键部分
  • HttpError(mock.Mock(status=404), 'not found') 是做什么的?对我来说,它没有将状态码设置为 404,它仍然是 None。
  • @Yang:你用的是google-api-client exception class吗?如果不详细说明您在做什么,我将无法帮助您。 mock.Mock(status=404) 表达式创建一个具有.status 属性的模拟对象,HttpError 将其存储为resp 属性。在问题中,然后以error.resp.status 访问。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-07
  • 2023-03-27
  • 2014-02-15
  • 1970-01-01
  • 1970-01-01
  • 2018-01-16
  • 1970-01-01
相关资源
最近更新 更多