¿Cómo probar las clases de Python que dependen de argparse?

El siguiente pegado contiene fragmentos relevantes de tres archivos de Python separados. El primero es un script llamado desde la línea de comandos que crea una instancia de CIPuller dados ciertos argumentos. Lo que sucede es que se llama al script con algo como: script.py ci (otros argumentos que debe ser tragado por argparse).

El segundo es parte de una subclase llamada Puller . El tercero es parte de una subclase de Puller llamada CIPuller .

Esto funciona de maravilla, ya que se llama a la subclase correcta, y cualquier usuario que use los otros argumentos incorrectos puede ver los argumentos correctos para su subclase dada, más los argumentos generics de la superclase. (Aunque me informaron fuera de línea que quizás debería usar subcomandos argparse para esto).

Estoy atascado tratando de escribir exámenes para estas clases. Actualmente, necesito un ArgumentParser para crear una instancia de las clases, pero al probar no estoy creando una instancia de las cosas desde la línea de comandos, por lo tanto, mi ArgumentParser es inútil.

Intenté crear un ArgumentParser en el arnés de prueba para pasar al constructor CIPuller's en el código de prueba, pero si uso add_argument allí, argparse se queja de manera comprensible de los argumentos dobles (duplicados) cuando llama a add_argument en el constructor CIPuller .

¿Cuál sería un diseño adecuado para probar estas clases con argumentos?

 #!/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 eg minitest / feature', choices=['minitest', 'feature'], required=True ) parser.add_argument( '--app', help='App eg sapi / stats', choices=['sapi', 'stats'], required=True ) 

Desunión para argparse es complicado. Hay un archivo test/test_argparse.py que se ejecuta como parte de la test/test_argparse.py Python general. Pero tiene un arnés de prueba personalizado complicado para manejar la mayoría de los casos.

Hay tres problemas básicos: 1) llamar a parse_args con valores de prueba, 2) probar los argumentos resultantes, 3) probar errores.

Probar los args resultantes es relativamente fácil. Y la clase argparse.Namespace tiene un método simple __eq__ para que pueda probar un espacio de nombres contra otro.

Hay dos formas de probar las entradas. Uno es modificar el sys.argv . Inicialmente, sys.argv tiene cadenas para el probador.

 self.args = parser.parse_args() 

prueba sys.argv[1:] por defecto. Así que si cambias sys.argv puedes probar valores personalizados.

Pero también puedes dar a parse_args una lista personalizada. Los documentos argparse utilizan esto en la mayoría de sus ejemplos.

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

Si myarg es None , usa sys.argv[1:] . De lo contrario, utiliza esa lista personalizada.

Los errores de prueba requieren un método parse.error personalizado (ver documentos) o envolver parse_args en un bloque try/except que puede detectar una excepción sys.exit .

¿Cómo escribes las pruebas para la porción argparse de un módulo de python?

Python unittest para argparse

Pruebas unitarias argparse: suprimir el mensaje de ayuda

Unittest con argumentos de línea de comando

Uso de unittest para probar argparse – errores de salida