2009-01-08 6 views
14

Je suis en train de test unitaire du code qui ressemble à ceci:Comment est-ce que je me moque de la méthode Python OptionParser.error(), qui fait un sys.exit()?

def main(): 
    parser = optparse.OptionParser(description='This tool is cool', prog='cool-tool') 
    parser.add_option('--foo', action='store', help='The foo option is self-explanatory') 
    options, arguments = parser.parse_args() 
    if not options.foo: 
     parser.error('--foo option is required') 
    print "Your foo is %s." % options.foo 
    return 0 

if __name__ == '__main__': 
    sys.exit(main()) 

Avec le code qui ressemble à ceci:

@patch('optparse.OptionParser') 
def test_main_with_missing_p4clientsdir_option(self, mock_optionparser): 
    # 
    # setup 
    # 
    optionparser_mock = Mock() 
    mock_optionparser.return_value = optionparser_mock 
    options_stub = Mock() 
    options_stub.foo = None 
    optionparser_mock.parse_args.return_value = (options_stub, sentinel.arguments) 
    def parser_error_mock(message): 
     self.assertEquals(message, '--foo option is required') 
     sys.exit(2) 
    optionparser_mock.error = parser_error_mock 

    # 
    # exercise & verify 
    # 
    self.assertEquals(sut.main(), 2) 

J'utilise Michael Foord's Mock, et le nez pour exécuter les tests.

Quand je lance le test, je reçois:

File "/Users/dspitzer/Programming/Python/test-optparse-error/tests/sut_tests.py", line 27, in parser_error_mock 
    sys.exit(2) 
SystemExit: 2 

---------------------------------------------------------------------- 
Ran 1 test in 0.012s 

FAILED (errors=1) 

Le problème est que OptionParser.error fait un sys.exit (2), et ainsi de main() repose naturellement sur ce point. Mais nez ou unittest détecte le sys.exit (attendu) et échoue le test.

Je peux faire passer le test en ajoutant "return 2" sous l'appel parser.error() dans main() et en supprimant l'appel sys.exit() de parser_error_mock(), mais je trouve désagréable de modifier le code sous test pour permettre le passage d'un test. Y a-t-il une meilleure solution?

La mise à jour: df La réponse fonctionne, bien que l'appel correct soit "self.assertRaises (SystemExit, sut.main)".

Ce qui signifie que le test passe quel que soit le nombre dans sys.exit() dans parser_error_mock(). Est-il possible de tester le code de sortie?

BTW, le test est plus robuste si j'ajoute:

self.assertEquals(optionparser_mock.method_calls, [('add_option', ('--foo',), {'action': 'store', 'help': 'The foo option is self-explanatory'}), ('parse_args',(), {})]) 

à la fin.

Mise à jour 2: Je peux tester le code de sortie en remplaçant "self.assertRaises (SystemExit, sut.main)" avec:

try: 
    sut.main() 
except SystemExit, e: 
    self.assertEquals(type(e), type(SystemExit())) 
    self.assertEquals(e.code, 2) 
except Exception, e: 
    self.fail('unexpected exception: %s' % e) 
else: 
    self.fail('SystemExit exception expected') 
+2

Les premiers assertEquals sur votre mise à jour 2 est inutile, puisque la « sauf » ligne ci-dessus il n'attraper des exceptions SystemExit. – rbp

Répondre

1

Comme indiqué dans mes mises à jour de ma question, je devais modifier la réponse de dF à:

self.assertRaises(SystemExit, sut.main) 

... et je suis venu avec un extrait plus long pour tester le code de sortie.

[Note: J'accepté ma réponse, mais je vais supprimer cette réponse et accepter dF « s s'il met à jour son.]

12

Est-ce que ce travail au lieu de assertEquals?

self.assertRaises(SystemExit, sut.main, 2) 

Cela devrait attirer l'exception SystemExit et empêcher le script de mettre fin.

+0

Brillant! Je vous remercie. Mais voir ma mise à jour à la question. –

+0

J'ai "inaccepté" votre réponse, car ce n'est pas tout à fait correct. Mais puisque vous m'avez donné la perspicacité qui m'a conduit à la réponse (voir les mises à jour de la question), je vais vous donner quelques jours pour éditer votre réponse et ensuite je vais accepter. –

0

Ajouter @raises à votre fonction de test. JE.e:

from nose.tools import raises 

@raises(SystemExit) 
@patch('optparse.OptionParser') 
def test_main_with_missing_p4clientsdir_option(self, mock_optionparser): 
    # Setup 
    ... 
    sut.main() 
Questions connexes