web-dev-qa-db-fra.com

Tests unitaires des scripts bash

Nous avons un système qui a des scripts bash en plus du code Java. Puisque nous essayons de tester tout ce qui pourrait éventuellement casser et que ces scripts bash peuvent casser, nous voulons les tester. 

Le problème est qu'il est difficile de tester les scripts bash.

Existe-t-il un moyen ou une meilleure pratique de tester les scripts bash? Ou devrions-nous cesser d'utiliser des scripts bash et rechercher des solutions alternatives testables?

89
nimcap

J'ai eu la réponse suivante d'un groupe de discussion:

il est possible d'importer (inclure, peu importe) une procédure (fonction, quel que soit son nom) depuis un externe fichier. C'est la clé pour écrire un script de test: vous cassez votre script en procédures indépendantes qui peut ensuite être importé dans les deux votre script courant et vos tests script, et alors vous avez votre exécution le script soit aussi simple que possible.

Cette méthode s'apparente à une injection de dépendance pour les scripts et semble raisonnable. Il est préférable d’éviter les scripts bash et d’utiliser un langage plus testable et moins obscur.

26
nimcap

Il existe actuellement un cadre de test unit pour les scripts Shell . Je ne l'ai pas utilisé moi-même, mais ça vaut peut-être la peine de vérifier.

Des questions similaires ont déjà été posées: 

40
ire_and_curses

TAPEZ SUR - test de Bash conforme: Système de test automatisé Bash

TAP, le protocole Test Anything, est une interface textuelle simple entre les modules de test d'un faisceau de test. TAP a commencé sa vie en tant que partie intégrante du test harnais pour Perl, mais a maintenant des implémentations en C, C++, Python, PHP, Perl, Java, JavaScript, etc.

chauve-souris

20
Janus Troelsen

Epoxy est un framework de test Bash que j'ai principalement conçu pour tester d'autres logiciels, mais je l'utilise également pour tester les modules bash, y compris se [ et Carton .

Les principaux avantages sont une surcharge de codage relativement faible, une imbrication d’assertions illimitée et une sélection flexible d’assertions à vérifier.

J'ai fait une présentation presentation en la comparant à BeakerLib - un framework utilisé par certains chez Red Hat.

7
spbnick

Pourquoi dites-vous qu'il est "difficile" de tester les scripts bash?

Quel est le problème avec les wrappers de test comme:

 #!/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
 }
6
Jim Dennis

Je ne peux pas croire que personne ne parle de OSHT ! Il est compatible avec à la fois TAP et JUnit, il s’agit d’un pur Shell (c’est-à-dire qu’aucune autre langue n’est impliquée), il fonctionne aussi de manière autonome, et il est simple et direct.

Le test ressemble à ceci (extraits extraits de la page du projet):

#!/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

Un parcours simple:

$ 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

Le dernier test indique "pas correct", mais le code de sortie est 0 car il s'agit d'un TODO. On peut aussi mettre en mode verbeux:

$ 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

Renommez-le pour utiliser une extension .t et placez-le dans un sous-répertoire t. Vous pouvez utiliser prove(1) (une partie de Perl) pour l'exécuter:

$ 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

Définissez OSHT_JUNIT ou passez -j pour produire une sortie JUnit. JUnit peut également être combiné avec prove(1).

J'ai utilisé cette bibliothèque pour tester les fonctions en recherchant leurs fichiers, puis en exécutant des assertions avec IS/OK et leurs négatifs, ainsi que des scripts en utilisant RUN/NRUN. Pour moi, ce cadre offre le plus grand avantage pour le moins de frais généraux.

3
Daniel C. Sobral

Essayez assert.sh

source "./assert.sh"

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

J'espère que ça aide!

3
Mark

Nikita Sobolev a écrit un excellent article de blog comparant quelques environnements de test bash différents: Test des applications Bash

Pour les impatients: la conclusion de Nikita était d’utiliser Bats mais il semble que Nikita ait raté le projet Bats-core qui me semble être celui à utiliser à l’avenir car le projet original Bats n’a pas été activement maintenu depuis 2013.

3
cb2

Essayez bashtest . C'est un moyen simple de tester vos scripts. Par exemple, vous avez do-some-work.sh qui modifie certains fichiers de configuration. Par exemple, ajoutez la nouvelle ligne PASSWORD = 'XXXXX' au fichier de configuration /etc/my.cfg.

Vous écrivez des commandes bash ligne par ligne, puis vérifiez le résultat.

Installer:

pip3 install bashtest

Créer des tests est une simple écriture de commandes bash. 

Fichier 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

Exécuter des tests: 

bashtest *.bashtest

Vous pouvez trouver quelques exemples ici et ici

3
pahaz

J'aime assez Shell2junit , un utilitaire permettant de générer une sortie de type JUnit à partir de tests de script Bash. Cela est utile car le rapport généré peut ensuite être lu par des systèmes d'intégration continue, tels que les plug-ins JUnit pour Jenkins et Bamboo.

Bien que Shell2junit ne fournisse pas le cadre de script Bash complet tel que shunit2 , il vous permet de générer des rapports Nice des résultats des tests.

3
Steve HHH

Peut-être que cela peut être utilisé ou contribuer à

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

Destiné à écrire les résultats dans le protocole TAP, ce qui, à mon avis, convient aux environnements de confiance et à ceux qui souhaitent des environnements Shell J'imagine que certaines choses fonctionnent dans les environnements Shell, alors certains pourraient argumenter devraient être testés dans leur environnement Shell.

3
hrob

J'ai créé shellspec parce que je voulais un outil facile à utiliser et utile.

Il est écrit par le script POSIX pur. Il a testé avec de nombreux obus plus que shunit2. Il a des fonctionnalités puissantes que chauves-souris/chauve-souris.

Par exemple, prendre en charge les blocs imbriqués, les simulations faciles, les tests paramétrés, le numéro de ligne d'assertion, l'exécution par numéro de ligne, l'exécution parallèle, l'exécution aléatoire, le formateur TAP/JUnit, l'intégration de la couverture et des CI, le profileur, etc. .

Voir la démo sur la page du projet.

2
koichi nakashima

J'ai essayé beaucoup de solutions présentées ici, mais la plupart d'entre elles étant trop volumineuses et difficiles à utiliser, j'ai donc construit mon propre petit cadre de test: https://github.com/meonlol/t-bash

C’est juste un fichier du référentiel que vous pouvez simplement exécuter directement, avec un jeu de base d’assertions de style JUnit.

Je l’ai utilisé de manière professionnelle dans plusieurs projets internes et j’ai pu rendre nos scripts bash super stables et résistants à la régression.

1
leondepeon

Vous voudrez peut-être jeter un oeil sur bash_unit:

https://github.com/pgrange/bash_unit

0
pgrange

Jetez un coup d'œil à Outthentic , il est simple, extensible à de nombreux langages (Perl, Python, Ruby, Bash au choix) et à un framework multiplateforme (Linux, Windows) pour tester toutes les applications en ligne de commande.

0
Alexey Melezhik