2016-08-09 1 views
1

J'ai des problèmes pour tester les exceptions qui seraient levées dans un avec python 3.4. Je ne peux pas obtenir l'exécution des tests pour cette paix de code:Python 3: comment tester les exceptions avec?

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) 

Et voilà comment je veux le tester:

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) 

Mais il échoue - Sortie:

Failure 
... 
AssertionError: 0 != 1 
+0

Voulez-vous dire 'exceptions dans 'pysftp.Connection' instanciation' ou lorsque vous appelez' connection.something() '? – ForceBru

+0

Que voulez-vous dire par * avec un 'with' *? Qu'est-ce qui soulève l'exception? Le gestionnaire de contexte que vous créez pour l'utiliser avec 'with'? Ou le gestionnaire de contexte est-il supposé gérer une exception déclenchée dans le bloc géré par 'with'? –

+0

Vous créez l'exception comme effet secondaire de * création * de votre gestionnaire de contexte (l'appel 'pysftp.Connection (..)' le soulève). L'instruction 'with 'ne peut jamais l'utiliser comme gestionnaire de contexte, et le bloc géré par' with' n'est jamais exécuté. Donc, votre code exerce correctement l'instruction 'try: ... sauf .. ', mais vous n'avez besoin d'aucune connaissance du gestionnaire de contexte. Ce qui ne va pas, c'est comment le logger est appelé. –

Répondre

1

Votre objet sftp_mock.return_value n'est jamais appelé, donc le side_effect n'est jamais déclenché et aucune exception n'est déclenchée. Il ne serait appelé que si la valeur de retour de pysftp.Connection(...) était elle-même appelée à nouveau.

Définir l'effet secondaire directement sur la maquette:

sftp_mock.side_effect = ex 

Notez que maintenant l'expression pysftp.Connection(...) soulève l'exception et il n'importe plus que la valeur de retour de cette expression aurait été utilisée comme contexte gestionnaire dans une instruction with.

Notez que vos exceptions se plaindront de ne pas avoir d'arguments; passer cas de vos exceptions, pas le type:

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

résultats dans: 'TypeError: __init __() manque 2 arguments positionnels requis: 'hôte' et 'port' –

+0

@LarsGrundei: ah, votre * exception * soulève cela; vous devrez fournir des instances, pas la classe. Vous pouvez reproduire cette erreur avec 'raise pysftp.ConnectionException', par exemple. –

+0

Merci beaucoup, ça fonctionne :) –