2016-08-09 23 views
1

私はPython 3.4でwi​​thの中で発生する例外をテストするのに問題があります。Python 3:で例外をテストする方法は?

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 
+0

あなたは「'pysftp.Connection'インスタンスで例外」や' connection.somethingを() 'を呼び出す意味しますか? – ForceBru

+0

'with' *で*を意味しますか?何が例外を提起するのですか? 'with'で使用するために作成するコンテキストマネージャ?あるいは、コンテキストマネージャは、 'with'によって管理されているブロックで発生した例外を処理することになっていますか? –

+0

あなたのコンテキストマネージャの副作用として例外を発生させています( 'pysftp.Connection(..)'呼び出しで呼び出されます)。 'with'ステートメントは、それをコンテキストマネージャーとして使用することは決してなく、' with'によって管理されるブロックも実行されません。したがって、あなたのコードは 'try:... except ..'文を正しく実行していますが、コンテキストマネージャの知識は必要ありません。間違っているのは、ロガーがどのように呼び出されるかということです。 –

答えて

1

sftp_mock.return_valueオブジェクトが呼び出されることはないため、side_effectはトリガーされず、例外も発生しません。 戻り値pysftp.Connection(...)の場合は、それ自体が再度呼び出された場合にのみ呼び出されます。今pysftp.Connection(...)式が例外を発生させないと、もはや、その式の戻り値がコンテキストとして使用されていたであろうことを重要

sftp_mock.side_effect = ex 

注:モックに直接副作用を設定

マネージャーはwithステートメントで

あなたの例外は何も引数がないことについて不平を言うことに注意してください。 インスタンスであなたの例外のを通過し、ないタイプ:

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

の結果は次のようになります: 'TypeError:__init __()2つの必須の定位置引数 'host'と 'port'' –

+0

@LarsGrundei:あなたの*例外*クラスではなくインスタンスを提供する必要があります。 'raise pysftp.ConnectionException'などでエラーを再現することができます。 –

+0

ありがとう、作品:) –

関連する問題