Dans les années 1960, il était considéré comme une bonne pratique de base en génie logiciel de tester votre code au fur et à mesure que vous l’écriviez. Les pionniers du développement logiciel de cette époque étaient partisans de différents niveaux de tests ; certains préconisaient les tests  » unitaires  » et d’autres non, mais tous reconnaissaient l’importance de tester le code.

Les tests exécutables ont peut-être été introduits pour la première fois par Margaret Hamilton sur le projet Apollo au milieu des années 1960, où elle a été à l’origine d’un type de vérification des exécutables que nous appelons aujourd’hui  » analyse statique du code « . Elle l’appelait « logiciel d’ordre supérieur », c’est-à-dire un logiciel qui opère contre d’autres logiciels plutôt que directement contre le domaine du problème. Son logiciel d’ordre supérieur examinait le code source pour rechercher des modèles connus pour conduire à des problèmes d’intégration.

En 1970, les gens avaient largement oublié les tests exécutables. Bien sûr, les gens exécutaient des applications et les tripotaient ici et là à la main, mais tant que le bâtiment ne brûlait pas autour d’eux, ils considéraient que le code était « suffisamment bon ». Le résultat a été plus de 35 ans de code en production dans le monde entier qui est insuffisamment testé, et dans de nombreux cas ne fonctionne pas entièrement comme prévu, ou d’une manière qui satisfait ses clients.

L’idée que les programmeurs testent au fur et à mesure a fait un retour à partir du milieu des années 1990, bien que jusqu’à aujourd’hui, la grande majorité des programmeurs ne le font toujours pas. Les ingénieurs d’infrastructure et les administrateurs système testent leurs scripts avec encore moins de diligence que les programmeurs testent leur code d’application.

Alors que nous entrons dans une ère où le déploiement rapide de solutions compliquées comprenant de nombreux composants autonomes devient la norme, et que les infrastructures « en nuage » nous obligent à gérer des milliers de VM et de conteneurs allant et venant à une échelle qui ne peut être gérée par des méthodes manuelles, l’importance des tests et des vérifications exécutables et automatisés tout au long du processus de développement et de livraison ne peut être ignorée ; pas seulement pour les programmeurs d’applications, mais pour tous ceux qui sont impliqués dans le travail informatique.

Avec l’avènement du devops (pollinisation croisée des compétences, méthodes et outils de développement et d’exploitation), et des tendances telles que  » l’infrastructure en tant que code  » et  » tout automatiser « , les tests unitaires sont devenus une compétence de base pour les programmeurs, les testeurs, les administrateurs système et les ingénieurs d’infrastructure.

Dans cette série de billets, nous présenterons l’idée de tester unitairement les scripts shell, puis nous explorerons plusieurs frameworks de test unitaire qui peuvent aider à rendre cette tâche pratique et durable à l’échelle.

Une autre pratique qui peut être peu familière à de nombreux ingénieurs d’infrastructure est le contrôle de version. Plus tard dans cette série, nous aborderons les systèmes de contrôle de version et les flux de travail que les développeurs d’applications utilisent, et qui peuvent être efficaces et utiles pour les ingénieurs d’infrastructure, également.

Un script à tester

Vivek Gite a publié un exemple de script shell pour surveiller l’utilisation du disque et générer une notification par courriel lorsque certains systèmes de fichiers dépassent un seuil. Son article est ici : https://www.cyberciti.biz/tips/shell-script-to-watch-the-disk-space.html. Utilisons-le comme sujet de test.

La version initiale de son script, avec l’ajout de l’option -P sur la commande df pour empêcher les sauts de ligne dans la sortie, comme suggéré dans un commentaire de Per Lindahl, ressemble à ceci:

#!/bin/shdf -HP | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{ print  " "  }' | while read output;do usep=$(echo $output | awk '{ print }' | cut -d'%' -f1 ) partition=$(echo $output | awk '{ print  }' ) if ; then echo "Running out of space \"$partition ($usep%)\" on $(hostname) as on $(date)" | mail -s "Alert: Almost out of disk space $usep%" [email protected] fidone

Vivek continue à affiner le script au-delà de ce point, mais cette version servira les objectifs du présent post.

Vérifications fonctionnelles automatisées

Un couple de règles empiriques concernant les vérifications fonctionnelles automatisées, que nous vérifions du code d’application ou un script ou toute autre sorte de logiciel :

  • la vérification doit s’exécuter de manière identique à chaque fois, sans aucune modification manuelle nécessaire pour préparer chaque exécution ; et
  • le résultat ne peut pas être vulnérable aux changements de l’environnement d’exécution, ou des données, ou d’autres facteurs externes au code testé.

Pass, Fail, et Error

Vous pourriez souligner qu’il est possible que le script ne s’exécute pas du tout. C’est normal pour toute sorte de cadre de test unitaire pour toute sorte d’application. Trois résultats, plutôt que deux, sont possibles :

  • Le code testé présente le comportement attendu
  • Le code testé s’exécute, mais ne présente pas le comportement attendu
  • Le code testé ne s’exécute pas

Pour des raisons pratiques, le troisième résultat est le même que le deuxième ; nous devrons trouver ce qui n’a pas fonctionné et le réparer. Donc, nous pensons généralement à ces choses comme étant binaires : Réussir ou échouer.

Que devrions-nous vérifier ?

Dans ce cas, nous sommes intéressés à vérifier que le script se comportera comme prévu compte tenu de diverses valeurs d’entrée. Nous ne voulons pas polluer nos contrôles unitaires avec d’autres vérifications au-delà de cela.

En examinant le code sous test, nous voyons que lorsque l’utilisation du disque atteint un seuil de 90%, le script appelle mail pour envoyer une notification à l’administrateur système.

En accord avec les bonnes pratiques généralement acceptées pour les vérifications unitaires, nous voulons définir des cas séparés pour vérifier chaque comportement que nous attendons pour chaque ensemble de conditions initiales.

En mettant notre chapeau de « testeur », nous voyons que c’est une sorte de condition limite. Nous n’avons pas besoin de vérifier de nombreux pourcentages différents d’utilisation du disque individuellement. Nous avons seulement besoin de vérifier le comportement aux limites. Par conséquent, l’ensemble minimal de cas pour fournir une couverture significative sera :

  • Il envoie un courriel lorsque l’utilisation du disque atteint le seuil
  • Il n’envoie pas de courriel lorsque l’utilisation du disque est inférieure au seuil

Qu’est-ce que nous ne devrions pas vérifier ?

En accord avec les bonnes pratiques généralement acceptées pour l’isolation des tests unitaires, nous voulons nous assurer que chacun de nos cas peut échouer pour exactement une raison : Le comportement attendu ne se produit pas. Dans la mesure du possible, nous voulons mettre en place nos vérifications afin que d’autres facteurs ne fassent pas échouer le cas.

Il n’est pas toujours rentable (ou même possible) de garantir que les facteurs externes n’affecteront pas nos vérifications automatisées. Il y a des moments où nous ne pouvons pas contrôler un élément externe, ou lorsque le faire impliquerait plus de temps, d’efforts et de coûts que la valeur de la vérification, et/ou implique un cas limite obscur qui a une très faible probabilité de se produire ou un très faible impact lorsqu’il se produit. C’est une question qui relève de votre jugement professionnel. En règle générale, faites de votre mieux pour éviter de créer des dépendances sur des facteurs au-delà de la portée du code testé.

Nous n’avons pas besoin de vérifier que les commandes df, grep, awk, cut et mail fonctionnent. C’est hors de portée pour nos objectifs. Celui qui maintient les utilitaires est responsable de cela.

Nous voulons savoir si la sortie de la commande df n’est pas traitée comme nous l’attendons par grep ou awk. Par conséquent, nous voulons que les vraies commandes grep et awk s’exécutent dans nos vérifications, en fonction de la sortie de la commande df qui correspond à l’intention de chaque cas de test. C’est dans la portée parce que les arguments de la ligne de commande de df font partie du script, et le script est le code sous test.

Cela signifie que nous aurons besoin d’une fausse version de la commande df à utiliser avec nos vérifications unitaires. Ce genre de faux composant est souvent appelé un mock. Un mock se substitue à un composant réel et fournit une sortie prédéfinie pour piloter le comportement du système de manière contrôlée, afin que nous puissions vérifier le comportement du code testé de manière fiable.

Nous voyons que le script envoie une notification par courriel lorsqu’un système de fichiers atteint le seuil d’utilisation. Nous ne voulons pas que nos vérifications unitaires crachent un tas de courriels inutiles, donc nous voudrons également simuler la commande mail.

Ce script est un bon exemple pour illustrer la simulation de ces commandes, car nous le ferons d’une manière différente pour mail que pour df.

Simuler la commande df

Le script est construit autour de la commande df. La ligne pertinente du script est :

df -HP | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{ print  " "  }'

Si vous exécutez juste df -HP, sans passer par grep, vous verrez une sortie similaire à ceci :

Filesystem Size Used Avail Use% Mounted onudev 492M 0 492M 0% /devtmpfs 103M 6.0M 97M 6% /run/dev/sda1 20G 9.9G 9.2G 52% /tmpfs 511M 44M 468M 9% /dev/shmtmpfs 5.3M 0 5.3M 0% /run/locktmpfs 511M 0 511M 0% /sys/fs/cgrouptmpfs 103M 8.2k 103M 1% /run/user/1000

Les commandes grep et awk réduisent la sortie à ceci :

0% udev52% /dev/sda1

Nous devons contrôler la sortie de df pour piloter nos cas de test. Nous ne voulons pas que le résultat de la vérification varie en fonction de l’utilisation réelle du disque sur le système où nous exécutons la suite de tests. Nous ne vérifions pas l’utilisation du disque, nous vérifions la logique du script. Lorsque le script est exécuté en production, il vérifie l’utilisation du disque. Ce que nous faisons ici est pour la validation, pas pour les opérations de production. Par conséquent, nous avons besoin d’une fausse commande df ou « mock » avec laquelle nous pouvons générer les « données de test » pour chaque cas.

Sur une plateforme *nix, il est possible de remplacer la vraie commande df en définissant un alias. Nous voulons que la commande aliasée émette des valeurs de test dans le même format que la sortie de df -HP. Voici une façon de le faire (il s’agit d’une seule ligne ; elle est séparée ci-dessous pour plus de lisibilité):

alias df="shift;echo -e 'Filesystem Size Used Avail Use% Mounted on'; echo -e 'tempfs 511M 31M 481M 6% /dev/shm'; echo -e '/dev/sda1 20G 9.9G 9.2G 52% /'"

Le décalage ignore l’argument ‘-HP’ lorsque le script s’exécute, de sorte que le système ne se plaindra pas que -HP est une commande inconnue. La commande df aliasée émet une sortie sous la même forme que df -HP.

Les valeurs de test sont canalisées dans grep puis awk lorsque le script s’exécute, ainsi nous ne simulons que le minimum nécessaire pour contrôler nos cas de test. Nous voulons que notre scénario de test soit aussi proche que possible de la « vraie chose » afin de ne pas obtenir de faux positifs.

Les mocks créés via une bibliothèque de mocking peuvent retourner une valeur prédéfinie lorsqu’ils sont appelés. Notre approche du mocking de la commande df reflète cette fonction d’un mock ; nous spécifions une sortie prédéfinie à retourner chaque fois que le code testé appelle df.

Mocking de la commande mail

Nous voulons savoir si le script essaie d’envoyer un email dans les bonnes conditions, mais nous ne voulons pas qu’il envoie un vrai email où que ce soit. Par conséquent, nous voulons aliaser la commande mail, comme nous l’avons fait pour la commande df plus tôt. Nous devons mettre en place quelque chose que nous pouvons vérifier après chaque cas de test. Une possibilité est d’écrire une valeur dans un fichier lorsque mail est appelé, puis de vérifier cette valeur dans notre scénario de test. Ceci est montré dans l’exemple ci-dessous. D’autres méthodes sont également possibles.

Les mocks créés via une bibliothèque de mocking peuvent compter le nombre de fois où ils sont appelés par le code testé, et nous pouvons affirmer le nombre attendu d’invocations. Notre approche du mocking de la commande mail reflète cette fonction d’un mock ; si le texte « mail » est présent dans le fichier mailsent après que nous ayons exécuté le script diskusage.sh, cela signifie que le script a bien appelé la commande mail.

Patron pour l’exécution de vérifications automatisées

Les vérifications automatisées ou exécutables à n’importe quel niveau d’abstraction, pour n’importe quel type d’application ou de script, dans n’importe quel langage, comprennent généralement trois étapes. Celles-ci portent généralement les noms suivants :

  • Arrangement
  • Act
  • Assert

La raison en est probablement que tout le monde aime l’allitération, en particulier sur la lettre A, puisque le mot « allitération » lui-même commence par la lettre A.

Quoi qu’il en soit, dans l’étape d’arrangement, nous établissons les préconditions pour notre cas de test. Dans l’étape act, nous invoquons le code à tester. Dans l’étape assert, nous déclarons le résultat que nous nous attendons à voir.

Lorsque nous utilisons un cadre de test ou une bibliothèque, l’outil gère l’étape assert de manière agréable pour nous, de sorte que nous n’avons pas besoin de coder beaucoup de logique if/else encombrante dans nos suites de test. Pour notre exemple initial, nous n’utilisons pas de framework ou de bibliothèque de test, nous vérifions donc les résultats de chaque cas avec un bloc if/else. Dans le prochain épisode, nous jouerons avec des cadres de tests unitaires pour les langages shell, et nous verrons à quoi cela ressemble.

Voici notre script de test grossier mais efficace pour tester le script shell de Vivek, que nous avons nommé diskusage.sh:

#!/bin/bashshopt -s expand_aliases# Before allalias mail="echo 'mail' > mailsent;false"echo 'Test results for diskusage.sh' > test_resultstcnt=0# It does nothing when disk usage is below 90%# Before (arrange)alias df="echo 'Filesystem Size Used Avail Use% Mounted on';echo '/dev/sda2 100G 89.0G 11.0G 89% /'"echo 'no mail' > mailsent# Run code under test (act). ./diskusage.sh# Check result (assert)((tcnt=tcnt+1))if ]; then echo "$tcnt. FAIL: Expected no mail to be sent for disk usage under 90%" >> test_resultselse echo "$tcnt. PASS: No action taken for disk usage under 90%" >> test_resultsfi # It sends an email notification when disk usage is at 90%alias df="echo 'Filesystem Size Used Avail Use% Mounted on';echo '/dev/sda1 100G 90.0G 10.0G 90% /'"echo 'no mail' > mailsent. ./diskusage.sh((tcnt=tcnt+1))if ]; then echo "$tcnt. PASS: Notification was sent for disk usage of 90%" >> test_resultselse echo "$tcnt. FAIL: Disk usage was 90% but no notification was sent" >> test_resultsfi # After allunalias dfunalias mail# Display test results cat test_results

Voici un parcours du script de test.

D’abord, vous voyez que nous utilisons bash pour tester un bon vieux fichier .sh. C’est parfaitement bien. Ce n’est pas nécessaire, mais c’est bien.

Puis, vous voyez une commande shopt. Cela va faire en sorte que le shell développe nos alias de test lorsque le sous-shell est invoqué pour exécuter le script diskusage.sh. Dans la plupart des cas d’utilisation, nous ne passerions pas les alias dans les sous-shells, mais les tests unitaires sont une exception.

Le commentaire, « Before all », est destiné aux personnes qui sont familières avec les frameworks de tests unitaires qui ont des commandes de mise en place et de démantèlement. Celles-ci sont souvent nommées quelque chose comme « avant » et « après », et il y a généralement une paire qui met entre parenthèses l’ensemble de la suite de tests et une autre paire qui est exécutée individuellement pour chaque cas de test.

Nous voulions montrer que la définition de l’alias pour le courrier, l’initialisation du fichier de résultats de test et l’initialisation du compteur de cas de test sont toutes faites exactement une fois, au début de la suite de tests. Ce genre de chose est normal dans les suites de tests exécutables. Le fait que nous testions un script shell au lieu d’un programme d’application n’y change rien.

Le commentaire suivant, « Il ne fait rien… » indique le début de notre premier cas de test individuel. La plupart des frameworks de tests unitaires offrent un moyen de fournir un nom à chaque cas, afin que nous puissions garder une trace de ce qui se passe et que d’autres outils puissent rechercher, filtrer et extraire des cas de test pour diverses raisons.

Puis, il y a un commentaire qui se lit comme suit : « Avant (mise en place) ». Celui-ci représente la mise en place qui s’applique juste au cas de test unique. Nous configurons l’alias df pour qu’il émette la sortie dont nous avons besoin pour ce cas particulier. Nous écrivons également le texte, « no mail », dans un fichier. C’est ainsi que nous serons en mesure de dire si le script diskusage.sh a tenté d’envoyer un courriel de notification.

L’étape d’action vient ensuite, où nous exerçons le code testé. Dans ce cas, cela signifie exécuter le script diskusage.sh lui-même. Nous le sourçons au lieu de l’exécuter directement.

Maintenant nous faisons l’étape d’assert, que nous faisons de la manière dure dans cet exemple parce que nous n’avons pas encore introduit un cadre de test. Nous incrémentons le compteur de test afin de pouvoir numéroter les cas de test dans le fichier de résultats. Sinon, si nous avions un grand nombre de cas, il serait difficile de savoir lesquels ont échoué. Les frameworks de test gèrent cela pour nous.

L’alias que nous avons défini pour la commande mail écrit le texte ‘mail’ dans le fichier mailsent. Si diskusage.sh appelle mail, alors le fichier mailsent contiendra ‘mail’ au lieu de la valeur initiale, ‘no mail’. Vous pouvez voir quelles sont les conditions de réussite et d’échec en lisant les chaînes de caractères répercutées dans le fichier de résultats de test.

En commençant par le commentaire, « Il envoie une notification par courriel… », nous répétons les étapes arrange, act, assert pour un autre scénario de test. Nous ferons en sorte que notre fausse commande df émette des données différentes cette fois-ci, pour piloter un comportement différent du code testé.

Là où apparaît le commentaire « After all », nous faisons le ménage après nous-mêmes en éliminant les définitions que nous avons créées dans la configuration « Before all » près du haut du script de test.

Enfin, nous vidons le contenu du fichier test_results pour voir ce que nous avons obtenu. Il ressemble à ceci :

Test results for diskusage.sh1. PASS: No action taken for disk usage under 90%2. PASS: Notification was sent for disk usage of 90%

Pourquoi utiliser un cadre de test/une bibliothèque ?

Nous venons d’écrire quelques cas de tests unitaires pour un script shell sans utiliser de cadre de test, de bibliothèque de mocking ou de bibliothèque d’assertion. Nous avons découvert que les commandes système peuvent être mockées en définissant des alias (au moins sur les systèmes *nix), que les assertions peuvent être implémentées comme des déclarations conditionnelles, et que la structure de base d’un test unitaire est facile à mettre en place à la main.

Il n’était pas difficile de faire cela sans framework ou bibliothèque. Alors, quel est l’avantage ?

Les frameworks et les bibliothèques de test simplifient et standardisent le code de test et permettent des suites de test beaucoup plus lisibles que des scripts faits à la main contenant beaucoup d’instructions conditionnelles. Certaines bibliothèques contiennent des fonctionnalités supplémentaires utiles, comme la possibilité de piéger les exceptions ou la possibilité d’écrire des cas de test pilotés par des tableaux et des données. Certaines sont conçues pour prendre en charge des produits spécifiques qui intéressent les ingénieurs en infrastructure, tels que Chef et Puppet. Et certains incluent des fonctionnalités permettant de suivre la couverture du code et/ou de formater les résultats des tests sous une forme consommable par les outils du pipeline CI/CD, ou au moins par un navigateur Web.

Cadres de tests unitaires pour les scripts

Dans cette série, nous allons explorer plusieurs cadres de tests unitaires pour les scripts shell et les langages de script. En voici un aperçu :

  • shunit2 est un projet Open Source très solide avec une histoire de dix ans. Développé à l’origine par Kate Ward, ingénieur et responsable de la fiabilité des sites chez Google, basée à Zurich, il est activement développé et soutenu par une équipe de six personnes. Après des débuts modestes en tant que solution ponctuelle pour tester une bibliothèque de journalisation pour les scripts shell, il a été intentionnellement développé en un cadre de test unitaire polyvalent qui prend en charge plusieurs langages shell et systèmes d’exploitation. Il comprend un certain nombre de fonctionnalités utiles au-delà des simples assertions, notamment la prise en charge des tests pilotés par des données et des tableaux. Il utilise le style traditionnel d’assertions « assertThat ». Le site du projet contient une excellente documentation. Pour les tests unitaires polyvalents de scripts shell, c’est ma principale recommandation.
  • BATS (Bash Automated Testing System) est un cadre de test unitaire pour bash. Il a été créé par Sam Stephenson il y a environ sept ans, et a eu une douzaine de contributeurs. La dernière mise à jour remonte à quatre ans, mais il n’y a pas lieu de s’inquiéter, car ce type d’outil ne nécessite pas de mises à jour ou de maintenance fréquentes. BATS est basé sur le Test Anything Protocol (TAP), qui définit une interface textuelle cohérente entre les modules de tout type de harnais de test. Il permet une syntaxe propre et cohérente dans les cas de test, bien qu’il ne semble pas ajouter beaucoup de sucre syntaxique au-delà des simples instructions bash. Par exemple, il n’y a pas de syntaxe spéciale pour les assertions ; vous écrivez des commandes bash pour tester les résultats. En gardant cela à l’esprit, sa principale valeur réside peut-être dans l’organisation des suites de tests et des cas d’une manière logique. Notez également que le fait d’écrire des scripts de test en bash ne nous empêche pas de tester des scripts non-bash ; nous l’avons fait plus tôt dans ce billet. Le fait que la syntaxe BATS soit si proche de la syntaxe bash nous donne beaucoup de flexibilité pour gérer différents langages shell dans nos suites de test, au prix d’une éventuelle perte de lisibilité (cela dépend de ce que vous trouvez « lisible » ; le public visé par ce billet trouve probablement la syntaxe du langage shell simple assez lisible). Une fonctionnalité particulièrement intéressante (à mon avis) est que vous pouvez configurer votre éditeur de texte avec la coloration syntaxique pour BATS, comme documenté sur le wiki du projet. Emacs, Sublime Text 2, TextMate, Vim et Atom étaient supportés à la date de ce billet.
  • zunit (pas celui d’IBM, l’autre) est un framework de test unitaire pour zsh développé par James Dinsdale. Le site du projet indique que zunit a été inspiré par BATS, et il inclut les variables très utiles $state, $output, et $lines. Mais il possède également une syntaxe d’assertion définitive qui suit le modèle « assert actual matches expected ». Chacun de ces frameworks a des caractéristiques uniques. Une caractéristique intéressante de ZUnit, à mon avis, est qu’il signalera comme « risqué » tout scénario de test qui ne contient pas d’assertion. Vous pouvez passer outre et forcer les cas à s’exécuter, mais par défaut, le framework vous aide à vous souvenir d’inclure une assertion dans chaque cas de test.
  • bash-spec est un framework de test de style comportemental qui supporte uniquement bash (ou du moins, il n’a été testé que contre des scripts bash). C’est un humble projet secondaire de ma part qui existe depuis plus de quatre ans et qui a quelques utilisateurs « réels ». Il n’est pas beaucoup mis à jour, car il fait actuellement ce pour quoi il a été conçu. Un des objectifs du projet était d’utiliser les fonctions bash dans un style « fluide ». Les fonctions sont appelées en séquence, chacune transmettant la liste complète des arguments à la suivante après avoir consommé le nombre d’arguments dont elle a besoin pour effectuer sa tâche. Le résultat est une suite de tests lisible, avec des instructions telles que « expect package-name to_be_installed » et « expect arrayname not to_contain value ». Lorsqu’il est utilisé pour guider le développement de scripts en fonction des tests, sa conception tend à amener le développeur à écrire des fonctions qui soutiennent l’idée de « modularité », de « responsabilité unique » ou de « séparation des préoccupations » (appelez cela comme vous voulez), ce qui facilite la maintenance et permet de réutiliser facilement les fonctions. « Style comportemental » signifie que les assertions prennent la forme, « s’attendre à ce que ceci corresponde à cela. »
  • korn-spec est un portage de bash-spec pour le shell korn.
  • Pester est le framework de test unitaire de choix pour Powershell. Powershell ressemble et se sent plus comme un langage de programmation d’applications que purement un langage de script, et Pester offre une expérience de développeur entièrement cohérente. Pester est livré avec Windows 10 et peut être installé sur tout autre système prenant en charge Powershell. Il dispose d’une bibliothèque d’assertions robuste, d’un support intégré pour mocking, et collecte des métriques de couverture de code.
  • ChefSpec s’appuie sur rspec pour fournir un cadre de test de style comportemental pour les recettes Chef. Chef est une application Ruby, et ChefSpec tire pleinement parti des capacités de rspec plus un support intégré pour les fonctionnalités spécifiques à Chef.
  • rspec-puppet est un framework de style comportemental pour Puppet, fonctionnellement similaire à ChefSpec.

admin

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.

lg