¿Cómo se debe probar / diseñar el código de un programa que se envía a la línea de comando?

7

Imagina un programa similar a este en Python:

import subprocess

class Example():

    _cmd_args = (['ls', '-a', '/usr/bin/'], ['ls', '-al', '/usr/local/bin/'])
    _default_args = 0

    def init(self):
        pass

    def run_ls_command(self):
        p = subprocess.Popen(self.get_command(), stdout=subprocess.PIPE)
        out, err = p.communicate()
        return err

    def get_command(self):
        return self._cmd_args[self._default_args]

    def set_args(self, args):
        self._default_args = args

    def do_magical_stuff(self):
        #do some stuff
        self._default_args = 0
        self.run_ls_command()

Este es un ejemplo simple, pero imagine un programa donde las "interacciones" primarias serán a través de la ejecución de comandos a través del subproceso contra el sistema operativo host.

Los comandos y argumentos específicos se construirán como resultado de una variedad de otras configuraciones y el estado del programa en el momento dado.

El código anterior no es verificable horriblemente.

Parece que podría ser mejor diseñarlo de tal manera que tenga objetos como:

class MyArgs():

    def __init__(self, _cmd, _cmd_args, _cmd_path):
        self.cmd = _cmd
        self.cmd_args = _cmd_args
        self.cmd_path = _cmd_path

    #more manipulation methods

    def get_command(self):
        return ([self.cmd, self.cmd_args, self.cmd_path])

y

class Example():

    args = MyArgs()

    def __init__(self):
        pass

    def run_ls_command(self):
        print self.get_command()
        p = subprocess.Popen(self.get_command(), stdout=subprocess.PIPE)
        out, err = p.communicate()
        return err

    def set_args(self, c, a, p):
        #this would be more complicated logic in future and likely not just one method
        self.args.set_args(c,a,p)

    def get_command(self):
        return self.args.get_command()

Entonces podría probar que MyArgs.get_command() funciona correctamente, directamente en la clase en sí misma, para sus entradas (especialmente porque dudo que esté construyendo un solo comando real "establecer todas las variables").

Eso me permitiría modificar mis pruebas contra Example para probar principalmente qué Example.get_command() devuelve cualquier otra manipulación que esté ocurriendo en el Ejemplo.

Esto todavía no es bueno para mí, ya que estoy probando lo que de otra manera deberían ser miembros privados, aparte de las pruebas, get_command debería ser privado.

Pretendo principalmente probar que los comandos correctos se activan, no si se ejecutan correctamente. Entonces, por ejemplo, si un usuario pasa ['ls', '-a', '/a/dir/that/does/not/exist'] , no me importa en este punto que ls fallará. Quiero validar el comando resultante (en realidad, la ejecución de los comandos llevará más tiempo del que quiero hacer para la unidad). Banco de pruebas). Saber en este caso que el Popen intentó ls -a /a/dir/that/does/not/exist es suficiente.

¿Hay una mejor manera de diseñar esta arquitectura para facilitar la capacidad de prueba?

    
pregunta enderland 19.02.2016 - 00:16

1 respuesta

2

Al final, lo que quieres verificar es que se llama a subprocess.Popen con los argumentos correctos. Para hacer eso, necesita una costura de prueba en el límite entre Example y la llamada a subprocess.Popen , para que pueda reemplazar Popen en sus pruebas con un simulacro.

No estoy lo suficientemente familiarizado con python para saber si puedes simular directamente Popen , pero si no puedes, entonces es bastante fácil escribir una pequeña clase de envoltorio que inyectas en Example . La versión de producción de este envoltorio solo pasa ciegamente todo a Popen , mientras que en tus pruebas puedes usar una versión simulada que te permite verificar que se pasaron los argumentos correctos.

La clase de envoltorio debe ser lo suficientemente simple / estúpida como para que una simple revisión de código pueda probar que no hay errores en el envoltorio, por lo que no es necesario que escriba pruebas para eso.

    
respondido por el Bart van Ingen Schenau 19.02.2016 - 13:48

Lea otras preguntas en las etiquetas