2016-03-25 12 views
5

Sono nello unit testing di alcuni script di shell legacy.Test dell'unità Script di shell: Come creare un programma di utilità complesso

Negli script del mondo reale sono spesso utilizzati per chiamare i programmi di utilità come find, tar, cpio, grep, sed, rsync, date e così via con alcune righe di comando piuttosto complesse che contengono un sacco di opzioni. A volte vengono costruite e utilizzate espressioni regolari o pattern jolly.

Un esempio : Uno script di shell che di solito è richiamato da cron ad intervalli regolari ha il compito di rispecchiare alcuni alberi di directory enormi da un computer all'altro utilizzando l'utilità rsync. diversi tipi di file e directory dovrebbero essere esclusi dal processo mirroring:

#!/usr/bin/env bash 
    ... 
    function mirror() { 
     ... 
     COMMAND="rsync -aH$VERBOSE$DRY $PROGRESS $DELETE $OTHER_OPTIONS \ 
        $EXCLUDE_OPTIONS $SOURCE_HOST:$DIRECTORY $TARGET" 
     ... 
     if eval $COMMAND 
     then ... 
     else ... 
     fi 
     ... 
    } 
    ... 

Come Michael Feathers ha scritto nel suo famoso libro Working Effectively with Legacy Code, una buona prova di unità corre molto veloce e non tocca la rete, il file- sistema o apre qualsiasi database.

Seguendo il consiglio di Michael Feathers la tecnica da utilizzare qui è: dependency injection. L'oggetto da sostituire qui è il programma di utilità rsync.

La mia prima idea: nel mio guscio framework di testing sceneggiatura (io uso bats) manipolo $PATH in un modo che un mockuprsync è trovato al posto del il vero rsync utility. Questo oggetto mockup potrebbe verificare i parametri e le opzioni della riga di comando forniti. Simile ad altre utilità utilizzate in questa parte di script under test.

Le mie esperienze passate con problemi reali in quest'area di scripting erano spesso errori causati da caratteri speciali nei nomi di file o directory, problemi di quoting o codifiche, chiavi ssh mancanti, autorizzazioni errate e così via. Questo tipo di bug sarebbe sfuggito a questa tecnica di test unitario. (Lo so: per alcuni di questi problemi il test dell'unità non è semplicemente la cura).

Un altro svantaggio è che scrivere un mockup per un programma di utilità complesso come rsync o find è soggetto ad errori e un compito di ingegneria noiosa propria.

Credo che la situazione descritta sopra sia abbastanza generale che altre persone potrebbero aver riscontrato problemi simili. Chi ha delle idee intelligenti e vorrebbe condividerle qui con me?

+3

Dopo unit testing, è necessario test di sistema. Forse costruisci una semplice rete di test con uno ciascuno dei file problematici, directory, chiavi mancanti, ecc. – tripleee

+1

Usa 'tee' in linea con uno scenario reale per catturare risposte di comando chiamate, quindi usa quei file per riprodurli nello scenario imbrigliato da test , forse con uno script che registra gli argomenti ricevuti e invia ciecamente la risposta predefinita. –

+0

@Keith Tyler: Questa è un'idea molto carina: ovviamente questa tecnica richiede una sorta di test di sistema scritto in anticipo (ma quando si inizia a giocherellare su uno script legacy, scrivere almeno alcuni di questi test prima di iniziare a ridefinire lo script è comunque un buon idea). Complimenti per questo suggerimento. – pefu

risposta

1

dilemma di Cargill:

"Qualsiasi problema di progettazione può essere risolto con l'aggiunta di un ulteriore livello di indirezione, tranne che per troppi livelli di indirezione"

Perché comandi di sistema fittizi? Dopotutto se si sta programmando Bash, il sistema è il tuo obiettivo di destinazione e dovresti valutare il tuo script usando il sistema.

Test di unità, come suggerisce il nome, vi darà una sicurezza in una parte unitaria del sistema che state progettando. Quindi dovrai definire qual è il tuo unità nel caso di uno script di bash.Una funzione ? Un file di script? Un comando ?

Dato che si desidera definire l'unità come funzione Vorrei quindi suggerire a scrivere un elenco di errori noti come avete elencato sopra:

  • caratteri speciali nei nomi di file o directory
  • Problemi con quoting o codifiche
  • chiavi ssh mancanti
  • Autorizzazioni errate e così via.

E scrivere un caso di test per questo. E prova a non deviare dai comandi di sistema, poiché sono integrali parte del sistema che stai consegnando.

0

È possibile mockup qualsiasi comando utilizzando una funzione, come questo:

function rsync() { 
    # mock things here if necessary 
} 

Poi esportare la funzione ed eseguire l'unittest:

export -f rsync 
unittest