2017-02-19 16 views
0

以下のペーストには、3つの別々のPythonファイルの関連スニペットが含まれています。最初は、コマンドラインから呼び出されたスクリプトで、特定の引数を指定してCIPullerをインスタンス化します。何が起こるかは、スクリプトが script.py ci(argparseによって他の引数が呑み込まれる)のように呼び出されるということです。argparseに依存するPythonクラスをテストするには?

第2のサブクラスはPullerというサブクラスの一部です。 3番目のサブクラスは、CIPullerと呼ばれるPullerのサブクラスの一部です。

正しいサブクラスが呼び出され、間違った他のargsを使用するユーザーは、指定されたサブクラスに適切なargsとスーパークラスの汎用引数を表示します。 (私はオフラインで、おそらくこのためにargparse sub-commandsを使用する必要があると考えましたが)。

これらのクラスのテストを作成しようとしていません。現在、クラスをインスタンス化するにはArgumentParserが必要ですが、テストではコマンドラインからインスタンス化しないため、ArgumentParserは役に立たないです。

私はテストコードにCIPuller'sコンストラクタに渡すテストハーネスでArgumentParserを作成しようとしたが、私はそこにadd_argumentを使用している場合、argparse当然のことながら、それはCIPullerコンストラクタでadd_argumentを呼び出すときの引数を(複製)ダブルについて文句を言います。

これらのクラスを引数でテストするには、どのような設計が適していますか?

#!/usr/bin/env python                

from ci_puller import CIPuller              
import argparse                 
import sys                   

# Using sys.argv[1] for the argument here, as we don't want to pass that onto  
# the subclasses, which should receive a vanilla ArgumentParser     
puller_type = sys.argv.pop(1)              

parser = argparse.ArgumentParser(             
    description='Throw data into Elasticsearch.'         
)                     

if puller_type == 'ci':               
    puller = CIPuller(parser, 'single')           
else:                    
    raise ValueError("First parameter must be a supported puller. Exiting.")  

puller.run()                  


class Puller(object):                

    def __init__(self, parser, insert_type):          
     self.add_arguments(parser)             
     self.args = parser.parse_args()           

     self.insert_type = insert_type            

    def add_arguments(self,parser): 
     parser.add_argument(              
      "-d", "--debug",              
      help="print debug info to stdout",          
      action="store_true"             
     )                   

     parser.add_argument(              
      "--dontsend",               
      help="don't actually send anything to Elasticsearch",     
      action="store_true"             
     )                   

     parser.add_argument(              
      "--host",                
      help="override the default host that the data is sent to",    
      action='store',              
      default='kibana.munged.tld'          
     )        

class CIPuller(Puller):               

    def __init__(self, parser, insert_type):          
     self.add_arguments(parser) 

     self.index_prefix = "code"             
     self.doc_type = "cirun"             

     self.build_url = ""              
     self.json_url = ""               
     self.result = []               

     super(CIPuller, self).__init__(parser, insert_type)      

    def add_arguments(self, parser):            
     parser.add_argument(              
      '--buildnumber',              
      help='CI build number',            
      action='store',              
      required=True               
     )                   

     parser.add_argument(              
      '--testtype',               
      help='Job type per CI e.g. minitest/feature',      
      choices=['minitest', 'feature'],          
      required=True               
     )                   

     parser.add_argument(              
      '--app',                
      help='App e.g. sapi/stats',           
      choices=['sapi', 'stats'],            
      required=True               
     )                   

答えて

3

ユニットテスト​​はトリッキーです。 Python unittest全体の一部として実行されるtest/test_argparse.pyファイルがあります。しかし、ほとんどのケースを処理する複雑なカスタムテストハーネスがあります。

基本的な問題は、1)parse_argsにテスト値を呼び出し、2)結果のargsをテストし、3)エラーをテストします。

結果として得られるargsのテストは比較的簡単です。そしてargparse.Namespaceクラスは簡単な__eq__メソッドを持っていますので、ある名前空間を別の名前空間でテストすることができます。

入力のテストには2通りの方法があります。 1つはsys.argvを変更することです。最初はsys.argvにテスター用の文字列があります。

self.args = parser.parse_args() 

sys.argv[1:]をデフォルトでテストします。したがって、sys.argvを変更すると、カスタム値をテストできます。

しかし、parse_argsにカスタムリストを与えることもできます。​​ドキュメントでは、ほとんどの例でこれを使用しています。 myargがある場合

self.args = parser.parse_args(argv=myargv) 

Noneそれはsys.argv[1:]を使用しています。それ以外の場合は、そのカスタムリストを使用します。

テストエラーカスタムparse.error方法(ドキュメントを参照)またはsys.exit例外をキャッチすることができtry/exceptブロックでparse_argsを包むのいずれかが必要です。

How do you write tests for the argparse portion of a python module?

python unittest for argparse

Argparse unit tests: Suppress the help message

Unittest with command-line arguments

Using unittest to test argparse - exit errors

+0

'テスト用sys.argv'を変更する最も簡単なようです。しかし、 'sys.argv'を変更したり、デフォルトであなたのカスタム' arvg'を使うための 'ArgumentParser'をmonkeypatchするのはもっと一般的な習慣です。 – Amir

+0

'' Puller'に、 'parse_args'に渡されるオプションの' argv'パラメータを取る別々の 'parse'メソッドを与えます。その後、通常の実行モードで、またはテスト値で使用できます。 'test_argparse'は両方のアプローチをテストします。 – hpaulj

+0

これは本当に役に立ちました。私が言及したリンクのいくつかを超えていたとしても、sys.argvを設定するという基本的な考えは私には起こりませんでした。時々、木の森は見えません。 :) – topper

関連する問題