【问题标题】:Python 3: how to tests exceptions within with?Python 3:如何在 with 中测试异常?
【发布时间】:2016-08-09 15:27:00
【问题描述】:

我在测试 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:
              http://stackoverflow.com/questions/37014904/mocking-python-class-in-unit-test-and-verifying-an-instance
              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

【问题讨论】:

  • 您的意思是“pysftp.Connection 实例化中的异常”还是调用connection.something() 时的异常?
  • 你说的with是什么意思?什么引发了异常?您创建用于with 的上下文管理器?还是上下文管理器应该处理由with 管理的块中引发的异常?
  • 您将引发异常作为创建上下文管理器的副作用(pysftp.Connection(..) 调用会引发它)。 with 语句永远不会将其用作上下文管理器,with 管理的块也不会被执行。因此,您的代码正确地执行了try:...except .. 语句,但您不需要任何上下文管理器知识。那么出现问题的是记录器是如何被调用的。
  • Foo.__init__ 究竟对第四个位置参数做了什么?它是被调用还是直接存储为self._log
  • @ForceBru 我的意思是 pysftp.Connection 中的异常

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


【解决方案1】:

永远不会调用您的 sftp_mock.return_value 对象,因此永远不会触发 side_effect 并且不会引发异常。只有当pysftp.Connection(...)返回值 本身被再次调用时才会调用它。

直接在mock上设置副作用

sftp_mock.side_effect = ex

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

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

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

【讨论】:

  • 结果:TypeError: __init__() missing 2 required positional arguments: 'host' and 'port'
  • @LarsGrundei:啊,你的 exception 提出了这一点;您必须提供实例,而不是类。例如,您可以使用 raise pysftp.ConnectionException 重现该错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多