Python3:如何测试with内的异常?

Python 3: how to tests exceptions within with?

我在测试 python 3.4 中的 with 中引发的异常时遇到问题。我只是无法获得这段代码的测试 运行:

import logging
...
class Foo(object):
    ...
    def foo(self, src, dst):
        try:
            with pysftp.Connection(self._host, username=self._username, password=self._password) as connection:
                connection.put(src, dst)
                connection.close()
        except (
                ConnectionException,
                CredentialException,
                SSHException,
                AuthenticationException,
                HostKeysException,
                PasswordRequiredException
        ) as e:
            self._log.error(e)

这就是我想要测试的方式:

import logging
...
class TestFoo(TestCase):
    @parameterized.expand([
        ('ConnectionException', ConnectionException),
        ('CredentialException', CredentialException),
        ('SSHException', SSHException),
        ('AuthenticationException', AuthenticationException),
        ('HostKeysException', HostKeysException),
        ('PasswordRequiredException', PasswordRequiredException),
    ])
    @patch('pysftp.Connection', spec_set=pysftp.Connection)
    def test_foo_exceptions(self, _, ex, sftp_mock):
        """
        NOTE: take a look at:
              
              to get an understanding of __enter__ and __exit__
        """
        sftp_mock.return_value = Mock(
            spec=pysftp.Connection,
            side_effect=ex,
            __enter__ = lambda self: self,
            __exit__ = lambda *args: None
        )
        foo = Foo('host', 'user', 'pass', Mock(spec_set=logging.Logger))
        foo.foo('src', 'dst')
        self.assertEqual(foo._log.error.call_count, 1)

但它失败了 - 输出:

Failure
...
AssertionError: 0 != 1

你的 sftp_mock.return_value 对象永远不会被调用,所以 side_effect 永远不会被触发,也不会引发异常。只有当 pysftp.Connection(...)return 值 本身被再次调用时才会被调用。

直接在 mock 上设置副作用 :

sftp_mock.side_effect = ex

请注意,现在 pysftp.Connection(...) 表达式引发了异常,并且该表达式的 return 值已被用作 with 语句中的上下文管理器不再重要.

注意你的异常会抱怨没有得到任何参数;传入异常的 个实例 ,而不是类型:

@parameterized.expand([
    ('ConnectionException', ConnectionException('host', 1234)),
    # ... etc.
])