9
votes

Comment écrire du code shell bash testable unitaire?

Dans l'universum OOP, il y a beaucoup d' informations sur la façon de concevoir et de refactoriser le code pour le rendre convivial pour les tests unitaires. Mais je me demande comment appliquer / traduire ces principes / pratiques (rendre les moqueries plus faciles, etc.) au script shell, qui est évidemment une programmation différente.

Je dois m'attaquer à une très grande base de code; de nombreuses procédures exécutables et non exécutables, des fonctions volumineuses, un grand état global, de nombreuses variables d'environnement et partout (inutiles) la communication interprocessus et la gestion des fichiers via la redirection / pipelines et l'utilisation (inutile) d'utilitaires externes.

Comment refactoriser le code shell (ou le concevoir au début) pour pouvoir faire de "bons" tests unitaires automatisés avec un framework comme les chauves-souris et un plugin mocking?


2 commentaires

Pourquoi bash utilisez-vous bash pour quelque chose d'aussi gros?


@chepner Vous n'arrivez généralement pas à décider comment les choses auraient dû être faites historiquement, c'est l'un des problèmes liés au manque de solutions pratiques pour le voyage dans le temps. :-)


3 Réponses :


1
votes

Bonne question!

Les shellscripts IMHO appellent souvent simplement d'autres programmes pour faire des choses, comme cp , mv , tar , rsync , ... même pour les expressions bash utilise le test binaire si vous utilisez [et] (par exemple, if [ -f $file ]; then; fi ).

En gardant cela à l'esprit, pensez à ce qui se passe vraiment dans le script bash: appelez ce programme avec trois arguments. Vous pouvez donc écrire des tests unitaires, qui vérifient si le script bash appelle le programme souhaité et utilise les bons arguments et vérifie les valeurs de retour / codes de sortie du programme.

Vous ne voulez certainement pas mettre des choses dans des tests unitaires pour votre script shell, ce qui est effectivement fait par un autre programme (par exemple, vérifier si rsync vraiment copié les fichiers de la machine A vers la machine B).

Juste mes deux cents


0 commentaires

1
votes

Les tests unitaires permettent de détecter les bogues dans le code isolé. Le code shell typique, cependant, est dominé par les interactions avec d'autres exécutables ou le système d'exploitation. Le type de problèmes qui réside dans les interactions dans le code shell va dans le sens de, est-ce que j'appelle les bons exécutables dans le bon ordre avec les arguments dans le bon ordre avec des valeurs d'argument correctement formatées, et sont les sorties sous la forme que je les attend pour être etc. Pour tester tout cela, vous ne devez pas appliquer de tests unitaires, mais plutôt des tests d'intégration.

Cependant, il existe un code shell adapté aux tests unitaires. C'est, par exemple, du code effectuant des calculs dans le shell ou des manipulations de chaînes. Je considérerais même le code shell avec des appels à certains outils fondamentaux comme basename comme approprié pour les tests unitaires (interpréter ces outils comme faisant partie de la «bibliothèque standard» si vous le souhaitez).

Comment rendre ces parties de code dans un shell qui conviennent pour être testées unitaires réellement testables avec des tests unitaires? L'une des approches les plus utiles de mon expérience est de séparer les interactions des calculs. Autrement dit, essayez de placer les parties de calcul dans des fonctions shell séparées à tester, ou d'extraire les parties dominées par l'interaction dans des fonctions shell séparées. Cela vous évite beaucoup d'efforts moqueurs.


0 commentaires

0
votes

TL; DR

Voici un référentiel de modèles * qui a des tests unitaires d'intégration continue de fichiers shell utilisant Travis-CI: https://github.com/at-0/shell_unit_testing_template

Puisque le repo pourrait un jour disparaître, voici l'idée de reproductibilité. (Notez que ce n'est pas nécessairement la meilleure façon de le faire, c'est juste une façon que j'ai trouvée de travailler).

Structure des fichiers

Les scripts shell se trouvent dans un dossier /src/ . Les tests unitaires se trouvent dans le dossier /test/ . Dans /src/ il y a un main.sh qui peut appeler d'autres scripts shell. Les autres scripts shell peuvent être constitués de fonctions testables séparément, par exemple le fichier active_function_string_manipulation.sh . (inclus ci-dessous)

Pour que cela fonctionne, j'avais besoin d'installer la prise en charge des fichiers de bats qui sont les fichiers de test unitaire. Cela a été fait avec le fichier install-bats-libs.sh avec le contenu:

language: bash

script:
    - ./test.sh

Script Shell

Un exemple de script shell dans /src/ is: active_function_string_manipulation.sh`.

#!./test/libs/bats/bin/bats

load 'libs/bats-support/load'
load 'libs/bats-assert/load'

@test "running the file in /src/active_function_string_manipulation.sh." {
    input="This Is a TEST"
    run ./src/active_function_string_manipulation.sh "This Is a TEST"
    assert_output "this is a test"
}

Test de l'unité

Les tests unitaires sont test.sh par un fichier appelé test.sh dans le répertoire racine du référentiel. Il a du contenu:

# Run this file to run all the tests, once
./test/libs/bats/bin/bats test/*.bats

Un exemple serait le test de: active_function_string_manipulation.sh avec: /test/test_active_function_string_manipulation.bats :

##################################################################
# Purpose: Converts a string to lower case
# Arguments:
#   $@ -> String to convert to lower case
##################################################################
function to_lower() 
{
    local str="$@"
    local output
    output=$(tr '[A-Z]' '[a-z]'<<<"${str}")
    echo $output
}
to_lower "$@"

Travis CI

Le Travis CI est implémenté à l'aide d'un fichier yml qui crée essentiellement un environnement et exécute les tests dans un environnement automatisé. Le fichier est nommé: .travis.yml et contient:

mkdir -p test/libs

git submodule add https://github.com/sstephenson/bats test/libs/bats
git submodule add https://github.com/ztombol/bats-support test/libs/bats-support
git submodule add https://github.com/ztombol/bats-assert test/libs/bats-assert

Divulgation*

Je suis impliqué dans la construction de ce référentiel , et c'est la mise en œuvre "pour les nuls / moi" des instructions de cet article .

Remarque

Je n'ai actuellement pas beaucoup d'informations sur la façon dont ce système évolue, et je ne suis actuellement pas en mesure d'estimer s'il a le potentiel d'être un système "prêt pour la production", ou s'il convient à de si grands projets, c'est simplement un environnement de test unitaire automatisé pour le code shell.


0 commentaires