TestCase - 模拟时 with 语句中未引发异常 __enter__

TestCase - Exception not raised in with statement when mocking __enter__

我正在尝试测试包含 with 语句的 Python 方法。 with 语句中的代码可以引发 RuntimeError。我正在谈论的测试测试 RuntimeError 是否被引发。

__enter____exit__ 方法很重(通常打开和关闭 SSH 连接),我在测试时模拟它们。

这里是我要测试的方法的简化定义:

# client.py
class Client():
    def method_to_test():
        with self:
            raise RuntimeError()

为了清楚起见,我省略了__enter____exit__的定义,并删除了method_to_test中所有与当前问题无关的代码。

为了测试此方法,我模拟了 __enter____exit__,并检查是否引发了 RuntimeError

# tests.py
from django.test import TestCase
import mock
from .client import Client

class ClientTestCase(TestCase):
    @mock.patch('mymodule.client.Client.__enter__')
    @mock.patch('mymodule.client.Client.__exit__')
    def test_method_raises_Runtime(self, mock_exit, mock_enter):
        mock_enter.return_value = None
        client = Client()
        with self.assertRaises(RuntimeError):
            client.method_to_test()

此测试失败:AssertionError: RuntimeError not raised

如果我不模拟 __enter__,则会引发 RuntimeError。为什么模拟 __enter__ 会使这个测试失败?

见于 PEP343 https://www.python.org/dev/peps/pep-0343/

The exception is swallowed if exit() returns true

由于self.__exit__Client.method_to_test中的一个MagicMockself.__exit__return是一个MagicMock,求值为TrueRuntimeError被吞了。

修复很简单。 self.__exit__() as to return None instead of a MagicMock:

# tests.py
from django.test import TestCase
import mock
from .client import Client

class ClientTestCase(TestCase):
    @mock.patch('mymodule.client.Client.__enter__')
    @mock.patch('mymodule.client.Client.__exit__')
    def test_method_raises_Runtime(self, mock_exit, mock_enter):
        # __exit__ returns None, evaluated as False
        mock_exit.return_value = None
        mock_enter.return_value = None
        client = Client()
        with self.assertRaises(RuntimeError):
            client.method_to_test()