2017-02-19 3 views
0

Вложенная паста содержит соответствующие фрагменты из трех отдельных файлов Python. Первый - это сценарий, который вызывается из командной строки, который передает CIPuller определенные аргументы. Что происходит, так это то, что скрипт вызывается с чем-то вроде: script.py ci (другие аргументы проглотили argparse).Как проверить классы Python, которые зависят от argparse?

Второй является частью подкласса Puller. Третий является частью подкласса Puller под названием CIPuller.

Это прекрасно работает, поскольку вызывается правильный подкласс, и любой пользователь, использующий неправильные другие аргументы, получает возможность видеть правильные аргументы для данного подкласса, а также общие аргументы суперкласса. (Хотя мне сообщили в автономном режиме, что, возможно, я должен использовать для этого argparse sub-commands.)

Я застреваю, пытаясь написать тесты для этих классов. В настоящее время мне нужен ArgumentParser для создания экземпляров классов, но при тестировании я не создаю объекты из командной строки, поэтому мой ArgumentParser бесполезен.

Я попытался создание ArgumentParser в тестовом жгуте, чтобы перейти к CIPuller's конструктору в тестовом коде, но если я использую 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

Unittesting for argparse сложно. Существует файл test/test_argparse.py, который запускается как часть общего Uittest Python. Но для большинства случаев у него есть сложный специальный тестовый жгут.

Существует три основные проблемы: 1) вызов parse_args с тестовыми значениями, 2) тестирование полученных аргументов, 3) тестирование ошибок.

Тестирование получившегося args относительно легко. И класс argparse.Namespace имеет простой метод __eq__, поэтому вы можете протестировать одно пространство имен против другого.

Существует два способа тестирования входов. Один из них - изменить sys.argv. Первоначально sys.argv имеет строки, предназначенные для тестера.

self.args = parser.parse_args() 

испытания sys.argv[1:] по умолчанию. Поэтому, если вы измените sys.argv, вы можете протестировать пользовательские значения.

Но вы также можете указать parse_args собственный заказ. Документы argparse используют это в большинстве своих примеров.

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

Если myarg является None он использует sys.argv[1:]. В противном случае он использует этот пользовательский список.

Тестирования ошибок требуют либо метода пользовательского parse.error (см документации) или обертывания parse_args в try/except блоке, который может поймать sys.exit исключения.

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' или monkeypatch' ArgumentParser', чтобы использовать ваш пользовательский 'arvg' по умолчанию? – Amir

+0

Дайте 'Puller' отдельный метод' parse', который принимает необязательный параметр 'argv', который передается' parse_args'. Затем вы можете использовать его в обычном режиме или с тестовыми значениями. 'test_argparse' проверяет оба подхода. – hpaulj

+0

Это было действительно полезно, хотя я уже упоминал некоторые ссылки, основная идея установки sys.argv мне не пришла в голову. Иногда мы не видим леса для деревьев. :) – topper

Смежные вопросы