Script bash di unit test


112

Abbiamo un sistema che ha alcuni script bash in esecuzione oltre al codice Java. Poiché stiamo cercando di testare tutto ciò che potrebbe rompersi e quegli script bash potrebbero non funzionare, vogliamo testarli.

Il problema è che è difficile testare gli script bash.

C'è un modo o una best practice per testare gli script bash? O dovremmo smettere di usare gli script bash e cercare soluzioni alternative che siano testabili?



possibile duplicato di Unit testing per script di shell
utente

Panoramica degli strumenti esistenti: medium.com/wemake-services/…
sobolevn

Risposte:


48

Esiste effettivamente uno shunit2 , un framework di unit test basato su xUnit per script di shell basati su Bourne. Non l'ho usato da solo, ma potrebbe valere la pena provarlo.

Domande simili sono state fatte prima:


3
Posso affermare (gioco di parole) che shunit2 (versione 2.1.6) è un po 'rotto fino ad oggi. AssertNull e assertNotNull non funzionano, anche se fornisci loro valori diretti. assertEquals funziona bene, ma penso che per ora dovrò tirare il mio.
labirinto

@labyrinth, sei sicuro che il problema non fosse un caso di questo: github.com/kward/shunit2/issues/53 "Come usare correttamente assertNull?"?
Victor Sergienko

1
@Victor È sicuramente possibile che non fossi abbastanza attento con le mie virgolette. Presto tornerò a un ruolo in cui shunit2 o qualche sistema di test di unità bash saranno molto utili. Proverò di nuovo.
labirinto

5
Sono un utente e talvolta contribuisco a shunit2 e posso confermare che il progetto è vivo e vegeto nel 2019.
Alex Harvey

31

Ho ricevuto la seguente risposta da un gruppo di discussione:

è possibile importare (includere, qualunque cosa) una procedura (funzione, qualunque sia il suo nome) da un file esterno. Questa è la chiave per scrivere uno script di test: suddividi il tuo script in procedure indipendenti che possono quindi essere importate sia nello script in esecuzione che nello script di test, e quindi lo script in esecuzione è il più semplice possibile.

Questo metodo è come l'inserimento di dipendenze per gli script e sembra ragionevole. È preferibile evitare gli script bash e usare un linguaggio più testabile e meno oscuro.


4
Non sono sicuro di dover votare su o giù, da un lato la divisione in parti più piccole va bene, ma dall'altro ho bisogno di un framework non di un set di script personalizzati
mpapis

10
Anche se non c'è niente di sbagliato in bash (ho scritto molti, molti script), è un linguaggio difficile da padroneggiare. La mia regola pratica è che se uno script è abbastanza grande da richiedere dei test, dovresti probabilmente passare a un linguaggio di scripting facilmente testabile.
Doug

1
Ma a volte è necessario disporre di qualcosa che possa essere reperito nella shell di un utente. Non mi è chiaro come lo faresti senza ricorrere a uno script di shell
Itkovian il

@Itkovian - potresti, ad esempio, usare npm per esportare un eseguibile nel percorso, quindi non è necessario alcun sourcing (il tuo pacchetto npm dovrà essere installato globalmente)
Eliran Malka

1
Seguirò il consiglio di non usare bash. :)
Maciej Wawrzyńczuk

30

Test Bash conforme a TAP : sistema di test automatizzato Bash

TAP, il Test Anything Protocol, è una semplice interfaccia basata su testo tra i moduli di test in un cablaggio di test. TAP è nato come parte del test harness per Perl, ma ora ha implementazioni in C, C ++, Python, PHP, Perl, Java, JavaScript e altri.


14
Vale la pena rivelare cos'è TAP e perché dovrebbe interessarci, altrimenti è solo un inutile copia-incolla
om-nom-nom

@ om-nom-nom: ora l'ho collegato al sito TAP.
Janus Troelsen

7
Dato che nessun altro stava dicendo l'indicibile: TAP = Test Anything Protocol
JW.

9

Nikita Sobolev ha scritto un eccellente post sul blog confrontando alcuni diversi framework di test bash: Test delle applicazioni Bash

Per gli impazienti: la conclusione di Nikita è stata quella di usare Bats, ma sembra che Nikita abbia perso il progetto Bats-core che mi sembra essere quello da utilizzare in futuro poiché il progetto originale Bats non è stato mantenuto attivamente dal 2013.


7

Epoxy è un framework di test Bash che ho progettato principalmente per testare altri software, ma lo uso anche per testare i moduli bash, inclusi se stesso e Carton .

I vantaggi principali sono l'overhead di codifica relativamente basso, l'annidamento illimitato delle asserzioni e la selezione flessibile delle asserzioni da verificare.

Ho fatto una presentazione confrontandolo con BeakerLib , un framework utilizzato da alcuni in Red Hat.


6

Perché dici che è "difficile" testare gli script bash?

Cosa c'è di sbagliato in wrapper di test come:

 #!/bin/bash
 set -e
 errors=0
 results=$($script_under_test $args<<ENDTSTDATA
 # inputs
 # go
 # here
 #
 ENDTSTDATA
 )
 [ "$?" -ne 0 ] || {
     echo "Test returned error code $?" 2>&1
     let errors+=1
     }

 echo "$results" | grep -q $expected1 || {
      echo "Test Failed.  Expected $expected1"
      let errors+=1
 }
 # and so on, et cetera, ad infinitum, ad nauseum
 [ "$errors" -gt 0 ] && {
      echo "There were $errors errors found"
      exit 1
 }

4
Primo, gli script bash non sono molto leggibili. In secondo luogo, le aspettative sono complicate come controllare se un file di blocco è stato creato con il PID dello script bash che lo ha creato.
nimcap

10
Ancora più importante, è difficile testare gli script di shell perché generalmente hanno un gran numero di effetti collaterali e utilizzano risorse di sistema come filesystem, rete, ecc. Idealmente, gli unit test sono privi di effetti collaterali e non dipendono dalle risorse di sistema.
jayhendren

4

Ho creato shellspec perché volevo uno strumento facile da usare e utile.

È scritto da puro script di shell POSIX. È stato testato con molte conchiglie più di shunit2. Ha caratteristiche potenti rispetto a bats / bats-core.

Ad esempio, supporta blocco annidato, facile da mock / stub, facile da saltare / in sospeso, test parametrizzati, numero di riga di asserzione, esecuzione per numero di riga, esecuzione parallela, esecuzione casuale, formattatore TAP / JUnit, copertura e integrazione CI, profiler e così via .

Guarda la demo nella pagina del progetto.


3

Mi piace abbastanza shell2junit , un'utilità per generare output simile a JUnit da test di script Bash. Ciò è utile perché il report generato può quindi essere letto da sistemi di integrazione continua, come i plug-in JUnit per Jenkins e Bamboo.

Sebbene shell2junit non fornisca il framework di scripting Bash completo come shunit2 , consente di avere un bel report dei risultati del test.


3

Prova bashtest . È un modo semplice per testare i tuoi script. Ad esempio, puoi do-some-work.shmodificare alcuni file di configurazione. Ad esempio, aggiungi una nuova riga PASSWORD = 'XXXXX'al file di configurazione/etc/my.cfg .

Scrivi i comandi bash riga per riga e poi controlla l'output.

Installare:

pip3 install bashtest

Creare test è solo scrivere comandi bash.

File test-do-some-work.bashtest:

# run the script  
$ ./do-some-work.sh > /dev/null

# testing that the line "PASSWORD = 'XXXXX'" is in the file /etc/my.cfg   
$ grep -Fxq "PASSWORD = 'XXXXX'" /etc/my.cfg && echo "YES"
YES

Esegui test:

bashtest *.bashtest

Puoi trovare alcuni esempi qui e qui


3

Forse questo può essere utilizzato o contribuito a

https://thorsteinssonh.github.io/bash_test_tools/

Destinato a scrivere i risultati nel protocollo TAP che immagino sia buono per CI e buono per coloro che desiderano ambienti shell. Immagino che alcune cose vengano eseguite in ambienti shell, quindi alcuni potrebbero sostenere che dovrebbero essere testate nel loro ambiente shell.


3

Prova a assert.sh

source "./assert.sh"

local expected actual
expected="Hello"
actual="World!"
assert_eq "$expected" "$actual" "not equivalent!"
# => x Hello == World :: not equivalent!

Spero che sia d'aiuto!


3

Non riesco a credere che nessuno abbia parlato di OSHT ! È compatibile con entrambi TAP che con JUnit, è pura shell (cioè, nessun altro linguaggio coinvolto), funziona anche da solo ed è semplice e diretto.

Il test ha questo aspetto (frammenti tratti dalla pagina del progetto):

#!/bin/bash
. osht.sh

# Optionally, indicate number of tests to safeguard against abnormal exits
PLAN 13

# Comparing stuff
IS $(whoami) != root
var="foobar"
IS "$var" =~ foo
ISNT "$var" == foo

# test(1)-based tests
OK -f /etc/passwd
NOK -w /etc/passwd

# Running stuff
# Check exit code
RUNS true
NRUNS false

# Check stdio/stdout/stderr
RUNS echo -e 'foo\nbar\nbaz'
GREP bar
OGREP bar
NEGREP . # verify empty

# diff output
DIFF <<EOF
foo
bar
baz
EOF

# TODO and SKIP
TODO RUNS false
SKIP test $(uname -s) == Darwin

Una corsa semplice:

$ bash test.sh
1..13
ok 1 - IS $(whoami) != root
ok 2 - IS "$var" =~ foo
ok 3 - ISNT "$var" == foo
ok 4 - OK -f /etc/passwd
ok 5 - NOK -w /etc/passwd
ok 6 - RUNS true
ok 7 - NRUNS false
ok 8 - RUNS echo -e 'foo\nbar\nbaz'
ok 9 - GREP bar
ok 10 - OGREP bar
ok 11 - NEGREP . # verify empty
ok 12 - DIFF <<EOF
not ok 13 - TODO RUNS false # TODO Test Know to fail

L'ultimo test mostra come "non ok", ma il codice di uscita è 0 perché è un TODO. Si può anche impostare verbose:

$ OSHT_VERBOSE=1 bash test.sh # Or -v
1..13
# dcsobral \!= root
ok 1 - IS $(whoami) != root
# foobar =\~ foo
ok 2 - IS "$var" =~ foo
# \! foobar == foo
ok 3 - ISNT "$var" == foo
# test -f /etc/passwd
ok 4 - OK -f /etc/passwd
# test \! -w /etc/passwd
ok 5 - NOK -w /etc/passwd
# RUNNING: true
# STATUS: 0
# STDIO <<EOM
# EOM
ok 6 - RUNS true
# RUNNING: false
# STATUS: 1
# STDIO <<EOM
# EOM
ok 7 - NRUNS false
# RUNNING: echo -e foo\\nbar\\nbaz
# STATUS: 0
# STDIO <<EOM
# foo
# bar
# baz
# EOM
ok 8 - RUNS echo -e 'foo\nbar\nbaz'
# grep -q bar
ok 9 - GREP bar
# grep -q bar
ok 10 - OGREP bar
# \! grep -q .
ok 11 - NEGREP . # verify empty
ok 12 - DIFF <<EOF
# RUNNING: false
# STATUS: 1
# STDIO <<EOM
# EOM
not ok 13 - TODO RUNS false # TODO Test Know to fail

Rinominalo per usare .tun'estensione e mettilo in una tsottodirectory, e puoi usare prove(1)(parte di Perl) per eseguirlo:

$ prove
t/test.t .. ok
All tests successful.
Files=1, Tests=13,  0 wallclock secs ( 0.03 usr  0.01 sys +  0.11 cusr  0.16 csys =  0.31 CPU)
Result: PASS

Impostare OSHT_JUNITo passare -jper produrre l'output JUnit. JUnit può anche essere combinato con prove(1).

Ho usato questa libreria sia per testare le funzioni reperendo i loro file e quindi eseguendo asserzioni con IS/ OKe i loro negativi, sia script usando RUN/ NRUN. Per me, questo framework fornisce il massimo guadagno con il minimo overhead.


1

Ho provato molte delle soluzioni presentate qui, ma ho trovato la maggior parte di esse ingombranti e difficili da usare, quindi ho creato il mio piccolo framework di test: https://github.com/meonlol/t-bash

È solo un file nel repository che puoi semplicemente eseguire direttamente, con un set di base di asserzioni in stile JUnit.

L'ho usato professionalmente in diversi progetti interni e sono riuscito a rendere i nostri script bash super stabili e resistenti alla regressione.



0

Dai un'occhiata a Outthentic , è semplice, estensibile da molti linguaggi (Perl, Python, Ruby, Bash a scelta) e framework multipiattaforma (Linux, Windows) per testare qualsiasi applicazione a riga di comando.


-2

Ho trovato difficile giustificare l'uso di bash per script più grandi quando Python ha vantaggi così enormi:

  • Try / Except consente di scrivere script più affidabili con la possibilità di annullare le modifiche in caso di errore.
  • Non è necessario utilizzare una sintassi oscura come " if [ x"$foo" = x"$bar"]; then ...", che è soggetta a errori.
  • Facile analisi di opzioni e argomenti utilizzando il getoptmodulo (e c'è un modulo ancora più semplice per analizzare gli argomenti, ma il nome mi sfugge).
  • Python ti consente di lavorare con elenchi / dict e oggetti invece che con stringhe e array di base.
  • Accesso a strumenti di linguaggio appropriati come regex, database (certo che potresti inserire tutto nel mysqlcomando in bash, ma non è il modo più carino per scrivere codice).
  • Non c'è bisogno di preoccuparsi di utilizzare la forma corretta di $*or "$*"or "$@"or $1or "$1", gli spazi nei nomi dei file non sono un problema, ecc., Ecc., Ecc.

Ora uso bash solo per gli script più semplici.


3
Non negando il fatto che Python abbia dei vantaggi, ma il tuo secondo punto non è molto ben espresso. Lo stesso confronto avrebbe potuto essere fatto come if [[ $foo = $bar ]]; then .... Questo non è ancora migliore di quello che Python ha da offrire, ma è migliore di quello che hai presentato.
Shrikant Sharat

8
Alcuni sistemi (incorporati per es.) Non hanno Python disponibile e non puoi / non vuoi installare materiale extra.
Rui Marques

2
Personalmente adoro bash, ma sono d'accordo sul fatto che possa essere un po 'stizzoso. Di solito devi essere molto più proattivo mentre in Python puoi risolvere gli errori dopo che si sono verificati. Tuttavia, bash ha trap(per pulire / annullare in caso di errore) così come regex (cioè [[ $1 =~ ^[1-3]{3}$ ]]). Sono abbastanza sicuro che l'oscura sintassi che hai usato sia in riferimento a vecchie implementazioni di test, non bash. Bash è ottimo per interfacciarsi con gli strumenti della riga di comando esistenti ... Spesso un singolo pipe awko grepè molto più semplice dell'alternativa Python.
Sei

1
A proposito, il modulo parser a cui ti riferivi è probabile optparseo il suo successore argparse. Non ho mai visto nessuno usare il getoptmodulo, né l'ho usato personalmente. L' getoptutilità è comunque ottima. L'analisi degli argomenti dalla shell non è affatto un problema una volta che hai un bel pattern. A meno che tu non stia cercando di implementare sottocomandi in stile git o qualcosa del genere, non è un gran problema.
Sei

Python non verrà eseguito ovunque dove bash possa raggiungere. Lo dico perché abbiamo testato bash contro python, la stessa logica del codice e abbiamo chiesto a entrambi di fare qualcosa. Bash è entrato in ogni directory a cui aveva accesso. D'altra parte, python non poteva gestire alcuni permessi e file di directory e anche directory che aumentavano e diminuivano molto velocemente.
vianna77
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.