
ChefSpec vérifie qu'une recette déclare les bonnes ressources, en mémoire, sans lancer aucune machine. Il simule la convergence de Chef puis vous laisse affirmer « cette recette installe bien nginx », « ce template notifie bien le rechargement du service ». Un cycle complet tourne en une fraction de seconde, ce qui en fait le premier filet de sécurité avant toute convergence sur une vraie VM. Ce guide teste le cookbook webstack du volet précédent. Public visé : lecteur ayant structuré un cookbook, à l'aise avec les ressources Chef.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Écrire un test ChefSpec pour une recette avec
spec_helperet unSoloRunner. - Vérifier qu'une ressource est déclarée avec ses propriétés (mode, contenu).
- Tester une notification entre ressources sans rien exécuter.
- Mesurer la couverture des ressources d'un cookbook.
- Situer ChefSpec, test unitaire, face à InSpec, test d'intégration.
Prérequis
Section intitulée « Prérequis »- Le cookbook
webstackdu guide Structurer un cookbook, avec ses recettesinstalletconfig. - CINC Workstation sur votre poste : ChefSpec y est déjà inclus, rien à installer.
Aucune VM n'est nécessaire : ChefSpec s'exécute entièrement sur votre poste. Testé avec ChefSpec 9.3.8 (CINC Workstation), cookbook webstack 1.0.0.
ChefSpec, le test unitaire de Chef
Section intitulée « ChefSpec, le test unitaire de Chef »Un cookbook mérite deux niveaux de tests, qui répondent à deux questions différentes. ChefSpec répond à « ma recette déclare-t-elle les bonnes ressources, avec les bonnes propriétés et les bonnes notifications ? ». Il compile la recette et joue la convergence en mémoire, sans toucher à un système. InSpec répond à « la machine est-elle réellement dans l'état voulu après convergence ? » et s'exécute sur la vraie VM.
| ChefSpec | InSpec | |
|---|---|---|
| Niveau | Unitaire | Intégration |
| Vérifie | Ce que la recette déclare | L'état réel de la machine |
| Cible | En mémoire, sur le poste | La VM convergée |
| Vitesse | Fraction de seconde | Dizaines de secondes |
| Attrape | Erreur de logique de recette | Écart entre intention et résultat |
Les deux sont complémentaires : ChefSpec avant de converger, pour un retour immédiat sur la logique ; InSpec après knife zero converge, pour prouver le résultat sur une vraie machine. Ce guide couvre le premier.
Écrire un premier test
Section intitulée « Écrire un premier test »ChefSpec s'appuie sur RSpec, le framework de test Ruby. On place les tests dans un dossier spec/ à la racine du cookbook. Un fichier spec_helper.rb charge ChefSpec une fois pour tous les tests.
-
Créer le helper
spec/spec_helper.rb:require "chefspec" -
Écrire le test de la recette
installdansspec/unit/recipes/install_spec.rb:require "spec_helper"describe "webstack::install" dolet(:chef_run) { ChefSpec::SoloRunner.new(platform: "ubuntu", version: "24.04").converge(described_recipe) }it "met a jour le cache apt" doexpect(chef_run).to update_apt_update("mise a jour du cache")endit "installe le paquet nginx" doexpect(chef_run).to install_package("nginx")endend
Trois éléments portent tout le test. Le SoloRunner simule un nœud : on lui donne une plateforme (ubuntu 24.04) pour que Chef connaisse les attributs par défaut de ce système, puis il converge la recette en mémoire. described_recipe reprend automatiquement le nom du describe (ici webstack::install). Enfin, les matchers comme install_package ou update_apt_update affirment qu'une ressource est déclarée avec la bonne action : le matcher install_package vérifie une ressource package dont l'action est :install.
Lancer les tests
Section intitulée « Lancer les tests »On exécute les tests avec chef exec rspec, qui utilise le RSpec embarqué dans CINC Workstation. Le drapeau --format documentation affiche chaque test par son intitulé.
cd ~/chef-parc/cookbooks/webstackchef exec rspec --format documentationLa sortie liste les tests réussis :
webstack::install met a jour le cache apt installe le paquet nginx
Finished in 0.16 seconds (files took 1.25 seconds to load)2 examples, 0 failuresDeux vérifications en 0,16 seconde, sans qu'aucune machine n'ait démarré. C'est cette rapidité qui permet de relancer les tests à chaque modification de la recette.
Tester la recette de configuration
Section intitulée « Tester la recette de configuration »La recette config est plus riche : un répertoire, un template, un fichier et un service. ChefSpec sait vérifier chaque ressource et ses propriétés. On crée spec/unit/recipes/config_spec.rb :
require "spec_helper"
describe "webstack::config" do let(:chef_run) { ChefSpec::SoloRunner.new(platform: "ubuntu", version: "24.04").converge(described_recipe) }
it "cree le repertoire racine" do expect(chef_run).to create_directory("/var/www/webstack").with(mode: "0755") end
it "depose le template de configuration nginx" do expect(chef_run).to create_template("/etc/nginx/conf.d/webstack.conf") end
it "cree la page index" do expect(chef_run).to create_file("/var/www/webstack/index.html").with(content: "webstack\n") end
it "active et demarre nginx" do expect(chef_run).to enable_service("nginx") expect(chef_run).to start_service("nginx") endendLa méthode .with(...) est la clé : elle ne vérifie pas seulement que la ressource existe, mais qu'elle porte les bonnes propriétés. Ici on prouve que le répertoire a le mode 0755 et que la page d'accueil contient bien le texte attendu. Un service se teste par deux matchers distincts, enable_service et start_service, car la recette déclare les deux actions.
Vérifier une notification
Section intitulée « Vérifier une notification »Le point le plus utile de ChefSpec est de tester ce qu'un simple coup d'oeil au code ne garantit pas : les notifications. La recette recharge nginx uniquement quand le template change. On vérifie ce lien sans exécuter quoi que ce soit, en ajoutant à config_spec.rb :
it "le template notifie le rechargement de nginx" do resource = chef_run.template("/etc/nginx/conf.d/webstack.conf") expect(resource).to notify("service[nginx]").to(:reload).delayedendOn récupère la ressource template, puis on affirme qu'elle notifie service[nginx] avec l'action :reload en mode :delayed. Si quelqu'un remplace un jour :reload par :restart, ou passe la notification en :immediately, ce test échoue immédiatement, avant même de toucher une machine.
Mesurer la couverture
Section intitulée « Mesurer la couverture »ChefSpec sait dire quelles ressources du cookbook ne sont couvertes par aucun test. On active le rapport dans spec_helper.rb :
require "chefspec"
RSpec.configure do |config| config.before(:suite) { ChefSpec::Coverage.start! }end
at_exit { ChefSpec::Coverage.report! }Au prochain chef exec rspec, un rapport s'ajoute à la fin :
8 examples, 0 failures
ChefSpec Coverage report generated...
Total Resources: 6 Touched Resources: 6 Touch Coverage: 100.0%Les 6 ressources du cookbook sont toutes touchées par au moins un test. Quand la couverture n'est pas complète, ChefSpec liste les ressources non testées avec leur fichier et leur ligne, ce qui pointe exactement où écrire le test manquant.
Voir un test échouer
Section intitulée « Voir un test échouer »Un test n'a de valeur que s'il échoue quand il le doit. Simulez une régression en affirmant, à tort, que la recette installe apache2 :
it "installe apache2" do expect(chef_run).to install_package("apache2")endChefSpec refuse et explique clairement l'écart :
Failures: 1) webstack::install installe apache2 Failure/Error: expect(chef_run).to install_package("apache2") expected "package[apache2]" with action :install to be in Chef run.
1 example, 1 failureC'est exactement ce qu'on attend d'un test : si une modification casse la logique de la recette, ChefSpec le signale en une fraction de seconde, avant la moindre convergence sur une VM. Retirez ensuite ce test volontairement faux.
Où ranger les tests
Section intitulée « Où ranger les tests »La convention Chef place les tests unitaires sous spec/unit/recipes/, un fichier *_spec.rb par recette. Cette arborescence est reconnue automatiquement par chef exec rspec, et un outil d'intégration continue la retrouve sans configuration. Gardez un spec_helper.rb unique à la racine de spec/ pour charger ChefSpec et activer la couverture.
À retenir
Section intitulée « À retenir »- ChefSpec teste ce qu'une recette déclare, en mémoire, sans lancer de VM.
- Un test tient en trois éléments : un
SoloRunner,described_recipe, et des matchers (install_package,create_template, ...). .with(...)vérifie les propriétés d'une ressource, pas seulement son existence.- Les notifications sont testables unitairement :
notify(...).to(:reload).delayed. - Le rapport de couverture révèle les ressources oubliées par les tests.
- ChefSpec est le test unitaire (avant convergence) ; InSpec est le test d'intégration (après
knife zero convergesur la vraie VM).