2016-08-18 9 views
4

Greg Haskin's answer in this questionをオフにして、choicesに存在しないargsを渡すときに、argparseが適切なエラーを出しているかどうかを確認するためにunittestを作成しようとしました。ただし、unittestは、以下のtry/exceptステートメントを使用して偽陽性を生成します。unittestを使用してargparse - exitエラーをテストする

さらに、with assertRaisesステートメントだけを使用してテストを行うと、​​は強制的にシステムを終了させ、プログラムはこれ以上テストを実行しません。

私はこのためのテストをしたいと思っていますが、エラーの際に​​が終了すると冗長かもしれませんか?

#!/usr/bin/env python3 

import argparse 
import unittest 

class sweep_test_case(unittest.TestCase): 
    """Tests that the merParse class works correctly""" 

    def setUp(self): 
     self.parser=argparse.ArgumentParser() 
     self.parser.add_argument(
      "-c", "--color", 
      type=str, 
      choices=["yellow", "blue"], 
      required=True) 

    def test_required_unknown_TE(self): 
     """Try to perform sweep on something that isn't an option. 
     Should return an attribute error if it fails. 
     This test incorrectly shows that the test passed, even though that must 
     not be true.""" 
     args = ["--color", "NADA"] 
     try: 
      self.assertRaises(argparse.ArgumentError, self.parser.parse_args(args)) 
     except SystemExit: 
      print("should give a false positive pass") 

    def test_required_unknown(self): 
     """Try to perform sweep on something that isn't an option. 
     Should return an attribute error if it fails. 
     This test incorrectly shows that the test passed, even though that must 
     not be true.""" 
     args = ["--color", "NADA"] 
     with self.assertRaises(argparse.ArgumentError): 
      self.parser.parse_args(args) 

if __name__ == '__main__': 
    unittest.main() 

エラー:

Usage: temp.py [-h] -c {yellow,blue} 
temp.py: error: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue') 
E 
usage: temp.py [-h] -c {yellow,blue} 
temp.py: error: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue') 
should give a false positive pass 
. 
====================================================================== 
ERROR: test_required_unknown (__main__.sweep_test_case) 
Try to perform sweep on something that isn't an option. 
---------------------------------------------------------------------- 
Traceback (most recent call last): #(I deleted some lines) 
    File "/Users/darrin/anaconda/lib/python3.5/argparse.py", line 2310, in _check_value 
    raise ArgumentError(action, msg % args) 
argparse.ArgumentError: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue') 

During handling of the above exception, another exception occurred: 

Traceback (most recent call last): #(I deleted some lines) 
    File "/anaconda/lib/python3.5/argparse.py", line 2372, in exit 
    _sys.exit(status) 
SystemExit: 2 
+0

'test/test_argparse.py'ユニットテストファイルには、モジュールのほとんどの機能をテストするため豊富な例があります。 'sys.exit'は特別な処理が必要です。 – hpaulj

+0

ありがとう@hpaulj、どこのシステムでそのファイルを見つけることができますか? [ここであなたが話していると思っているものが見つかりました](https://hg.python.org/cpython/file/default/Lib/test/test_argparse.py)。 – conchoecia

+0

はい、これがファイルです。 Pythonの開発版が必要な場合があります。 'Lib/test'ディレクトリを探します。しかし、リポジトリからのダウンロードも問題ありません。 'ParserTestCase'に構築されたテストのほとんどは、エラーメッセージについて心配していません。ケースが実行されるかどうかだけです。ファイルをさらに調べると、エラーメッセージが表示されます。 – hpaulj

答えて

3

が、それは正常にトラップされ、そしてparser.errorparse.exitに渡されます。その結果、使用法がエラーメッセージとともに印刷され、次にsys.exit(2)が印刷されます。

したがってasssertRaisesは、​​にこの種のエラーをテストする良い方法ではありません。モジュールのユニットテストファイルtest/test_argparse.pyは、これを回避するための精巧な方法を持っています。ArgumentParserをサブクラス化し、errorメソッドを再定義し、出力をリダイレクトします。

このテスト(I」についてどのように
try: 
     namespace, args = self._parse_known_args(args, namespace) 
     if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): 
      args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) 
      delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) 
     return namespace, args 
    except ArgumentError: 
     err = _sys.exc_info()[1] 
     self.error(str(err)) 

=================

parser.parse_known_argsは(parse_argsによって呼び出される)で終わります実行して

import argparse 
import unittest 

class ErrorRaisingArgumentParser(argparse.ArgumentParser): 
    def error(self, message): 
     #print(message) 
     raise ValueError(message) # reraise an error 

class sweep_test_case(unittest.TestCase): 
    """Tests that the Parse class works correctly""" 

    def setUp(self): 
     self.parser=ErrorRaisingArgumentParser() 
     self.parser.add_argument(
      "-c", "--color", 
      type=str, 
      choices=["yellow", "blue"], 
      required=True) 

    def test_required_unknown(self): 
     """Try to perform sweep on something that isn't an option. 
     Should pass""" 
     args = ["--color", "NADA"] 
     with self.assertRaises(ValueError) as cm: 
      self.parser.parse_args(args) 
     print('msg:',cm.exception) 
     self.assertIn('invalid choice', str(cm.exception)) 

if __name__ == '__main__': 
    unittest.main() 

test_argparse.pyからいくつかのアイデアを借用しまし

1931:~/mypy$ python3 stack39028204.py 
msg: argument -c/--color: invalid choice: 'NADA' (choose from 'yellow', 'blue') 
. 
---------------------------------------------------------------------- 
Ran 1 test in 0.002s 

OK 
+0

ありがとう、これは非常に便利です – conchoecia

1

エラー・ログを見ると、あなたがargparse.ArgumentErrorAttributeError上げとされていないことがわかります。あなたのコードは次のようになります。パーサは、特定の引数を解析中に例外ArgumentErrorを上げることができる

#!/usr/bin/env python3 

import argparse 
import unittest 
from argparse import ArgumentError 

class sweep_test_case(unittest.TestCase): 
    """Tests that the merParse class works correctly""" 

    def setUp(self): 
     self.parser=argparse.ArgumentParser() 
     self.parser.add_argument(
      "-c", "--color", 
      type=str, 
      choices=["yellow", "blue"], 
      required=True) 

    def test_required_unknown_TE(self): 
     """Try to perform sweep on something that isn't an option. 
     Should return an attribute error if it fails. 
     This test incorrectly shows that the test passed, even though that must 
     not be true.""" 
     args = ["--color", "NADA"] 
     try: 
      self.assertRaises(ArgumentError, self.parser.parse_args(args)) 
     except SystemExit: 
      print("should give a false positive pass") 

    def test_required_unknown(self): 
     """Try to perform sweep on something that isn't an option. 
     Should return an attribute error if it fails. 
     This test incorrectly shows that the test passed, even though that must 
     not be true.""" 
     args = ["--color", "NADA"] 
     with self.assertRaises(ArgumentError): 
      self.parser.parse_args(args) 

if __name__ == '__main__': 
    unittest.main() 
+0

提案していただきありがとうございます。あなたが提案したように、 'AttributeError'を' ArgumentError'に置き換えると、 'NameError:name 'ArgumentError'が定義されていません。 ArgumentErrorは一般的な名前空間にないので、これは意味があります。これは 'argparse'の一部です。私は 'AttributeError'を' argparse.ArgumentError'に置き換えようとしましたが、上記と同じエラーがありました。私はこれを反映するために私の質問を編集しました。 – conchoecia

1

argparse.py、1732行目(私のpythonバージョンは3.5.1)のargparseのソースコードを調べると、parse_known_argsというメソッドがあります。ArgumentParserです。コードは次のとおりです。だから、

# parse the arguments and exit if there are any errors 
try: 
    namespace, args = self._parse_known_args(args, namespace) 
    if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): 
     args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) 
     delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) 
    return namespace, args 
except ArgumentError: 
    err = _sys.exc_info()[1] 
    self.error(str(err)) 

ArgumentErrorは​​に飲み込まれ、エラーコードで終了します。とにかくこれをテストしたいのであれば、私が考えることができる唯一の方法は、sys.exc_infoを嘲笑することです。

関連する問題