Come deve essere testato / progettato il codice per un programma che viene inviato alla riga di comando?

7

Immagina un programma simile a questo in 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()

Questo è un semplice esempio - ma immagina un programma in cui le "interazioni" primarie saranno eseguite eseguendo i comandi attraverso il sottoprocesso contro il sistema operativo host.

I comandi e gli argomenti specifici verranno creati come risultato di una varietà di altre configurazioni e dello stato del programma in un dato momento.

Il codice sopra riportato è orribilmente non verificabile.

Sembra che potrebbe essere meglio progettarlo in modo tale da avere oggetti come:

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])

e

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()

Sarei quindi in grado di verificare che MyArgs.get_command() funzioni correttamente, direttamente sulla classe stessa, per i suoi input dati (specialmente perché dubito che stia costruendo un singolo comando "imposta tutte le variabili").

Questo mi permetterebbe di modificare i miei test rispetto a Example per testare principalmente cosa Example.get_command() restituisce qualunque altra manipolazione possa accadere ad Esempio.

Questo non è ancora bello per me, dal momento che sto testando quali dovrebbero essere i membri privati - a parte il test, get_command dovrebbe essere privato.

Intendo principalmente provare che i comandi corretti sono attivati, non se vengono eseguiti correttamente. Quindi, per esempio, se un utente passa in ['ls', '-a', '/a/dir/that/does/not/exist'] non mi interessa a questo punto che il ls fallirà - Voglio convalidare il comando risultante (in realtà l'esecuzione dei comandi richiederà più tempo di quello che voglio fare per l'unità test suite). Sapendo in questo caso che il Popen tentato ls -a /a/dir/that/does/not/exist è sufficiente.

C'è un modo migliore per progettare questa architettura per facilitare la testabilità?

    
posta enderland 19.02.2016 - 00:16
fonte

1 risposta

2

Alla fine, quello che vuoi verificare è che subprocess.Popen venga chiamato con gli argomenti giusti. Per fare ciò, hai bisogno di una giunzione di prova al confine tra Example e la chiamata a subprocess.Popen , in modo che tu possa sostituire Popen nei tuoi test con una simulazione.

Non ho familiarità con Python per sapere se è possibile simulare direttamente Popen , ma se non è possibile, allora è abbastanza facile scrivere una piccola classe wrapper che si inietta in Example . La versione di produzione di questo wrapper passa tutto ciecamente fino a Popen , mentre nei tuoi test puoi utilizzare una versione fittizia che ti consente di verificare che vengano passati gli argomenti giusti.

La classe wrapper dovrebbe essere semplice / stupida abbastanza che una semplice revisione del codice possa provare che non ci sono bug nel wrapper stesso, quindi non è necessario scrivere test per questo.

    
risposta data 19.02.2016 - 13:48
fonte

Leggi altre domande sui tag