0Pricing
DevOps Bootcamp · Leçon

Simuler des commandes et créer des doublures d’outils externes

Redéfinissez PATH et créez de faux binaires pour tester les scripts sans toucher aux systèmes réels.

Simuler des commandes et créer des doublures d’outils externes est une leçon DevOps Bootcamp gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage DevOps Bootcamp, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours DevOps Bootcamp comprend 4 leçons au total.

Pourquoi simuler des commandes dans les tests Bash ?

Lorsque vous testez un script Bash qui appelle curl, aws, git ou un autre outil externe, vous rencontrez un problème : les appels réels accèdent au réseau, modifient l’état du système, coûtent de l’argent ou échouent simplement dans un environnement d’intégration continue où ces outils ne sont pas installés.

La simulation consiste à remplacer la commande réelle par une fausse commande que vous contrôlez. Votre fausse commande (le bouchon) renvoie une sortie et des codes de sortie prévisibles ; votre test est ainsi rapide, isolé et reproductible.

  • Aucun accès au réseau ou au cloud n’est nécessaire
  • Les tests s’exécutent en quelques millisecondes plutôt qu’en quelques secondes
  • Vous pouvez simuler des erreurs difficiles à provoquer sur de vrais systèmes
  • Les chaînes d’intégration continue restent propres et sans dépendances

Bash fournit un mécanisme étonnamment simple pour y parvenir : placez simplement votre faux binaire dans un répertoire situé avant le véritable dans $PATH.

Fonctionnement de la recherche dans PATH

Lorsque le shell exécute une commande comme curl, il parcourt les répertoires de $PATH de gauche à droite et exécute la première correspondance trouvée.

Ainsi, si vous placez en tête un répertoire contenant votre propre script curl, le shell n’atteindra jamais /usr/bin/curl.

La méthode de remplacement :

  1. Créez un répertoire temporaire (votre répertoire de bouchons).
  2. Écrivez un exécutable factice portant le même nom que la commande réelle.
  3. Ajoutez ce répertoire au début de PATH.
  4. Exécutez le script testé : il appelle votre bouchon, et non le véritable binaire.
  5. Supprimez le répertoire temporaire après le test.

Cette méthode fonctionne sans accès administrateur, sans modifier les fichiers système et sans framework particulier.

Créer un répertoire de bouchons

La méthode standard consiste à utiliser mktemp -d pour créer un répertoire temporaire isolé destiné à vos bouchons. Chaque test ou suite de tests dispose ainsi de son propre répertoire, ce qui évite toute pollution entre les tests.

Une fois le test terminé, supprimez le répertoire avec rm -rf. L’utilisation d’un trap garantit le nettoyage, même si le test se termine prématurément à cause d’une erreur.

#!/usr/bin/env bash
# Setup a stub bin directory for testing

# Create the temp dir
STUB_BIN=$(mktemp -d)

# Always clean up on exit (success, error, or signal)
trap 'rm -rf "$STUB_BIN"' EXIT

# Prepend it to PATH so our stubs take priority
export PATH="$STUB_BIN:$PATH"

echo "Stub bin: $STUB_BIN"
echo "PATH starts with: ${PATH%%:*}"

# Your tests would go here...
echo "Tests complete."

Écrire votre premier bouchon

Un bouchon est simplement un fichier exécutable portant le même nom que la commande que vous souhaitez remplacer. Il affiche la sortie attendue par le script testé et se termine avec le code de votre choix.

Règles essentielles pour les bouchons :

  • Le fichier doit être exécutable (chmod +x).
  • La ligne de shebang (#!/usr/bin/env bash) est obligatoire.
  • Affichez la sortie que votre véritable script analyserait.
  • Utilisez exit 0 en cas de réussite et une valeur différente de zéro pour simuler un échec.
#!/usr/bin/env bash
# Create a stub for 'curl' that returns a fake HTTP response

STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"

# Write the stub
cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
# Fake curl: always returns a 200 OK with a JSON body
echo '{"status": "ok", "version": "1.2.3"}'
exit 0
EOF
chmod +x "$STUB_BIN/curl"

# Verify the stub is found before the real curl
which curl
curl https://example.com/api/version

Tester un script qui appelle curl

Mettons maintenant tout cela en pratique. Supposons que vous disposez d’un script de déploiement qui appelle curl pour vérifier un point de terminaison d’état, puis se termine avec une erreur si le service n’est pas sain. Vous voulez tester le cas nominal et le cas d’échec sans utiliser de serveur réel.

#!/usr/bin/env bash
# Script under test: check_health.sh
# It calls curl and checks the returned JSON

check_health() {
  local url="$1"
  local response
  response=$(curl -sf "$url")
  if [[ "$response" == *'"healthy":true'* ]]; then
    echo "Service is UP"
    return 0
  else
    echo "Service is DOWN" >&2
    return 1
  fi
}

# ---- Test harness ----
STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"

# Happy path stub
cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
echo '{"healthy":true}'
EOF
chmod +x "$STUB_BIN/curl"

check_health "http://fake-host/health" && echo "PASS: healthy response"

Simuler les échecs de commandes

L'une des utilisations les plus précieuses des simulacres consiste à simuler des échecs difficiles à reproduire avec de vrais outils : dépassements de délai réseau, erreurs d'autorisation, disque plein ou API distante renvoyant une erreur 500.

Pour simuler un échec, il suffit de faire quitter votre simulacre avec un code différent de zéro. Vous pouvez également écrire dans stderr exactement comme le ferait la vraie commande, afin de tester complètement la gestion des erreurs de votre script.

#!/usr/bin/env bash
# Test that check_health handles a curl failure gracefully

check_health() {
  local url="$1"
  local response
  # -f makes curl exit non-zero on HTTP error; -s silences progress
  if ! response=$(curl -sf "$url" 2>/dev/null); then
    echo "ERROR: could not reach $url" >&2
    return 1
  fi
  echo "OK: $response"
}

STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"

# Failure stub — simulates a network error (curl exit code 6 = could not resolve host)
cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
echo 'curl: (6) Could not resolve host: fake-host' >&2
exit 6
EOF
chmod +x "$STUB_BIN/curl"

if ! check_health "http://fake-host/health"; then
  echo "PASS: failure path handled correctly"
fi

Enregistrer les appels d'un simulacre pour les vérifier

Vous devez parfois vérifier non seulement ce que votre script produit, mais aussi la manière dont il appelle un outil externe : les arguments transmis, le nombre d'appels ou leur ordre. Un simulacre espion enregistre ses appels dans un fichier.

Après l'essai, votre harnais lit le fichier d'enregistrement et vérifie son contenu. Vous bénéficiez ainsi d'une vérification au niveau des arguments, sans cadre particulier.

#!/usr/bin/env bash
# Spy stub: record every invocation of 'aws' to a log file

STUB_BIN=$(mktemp -d)
CALL_LOG=$(mktemp)
trap 'rm -rf "$STUB_BIN" "$CALL_LOG"' EXIT
export PATH="$STUB_BIN:$PATH"
export CALL_LOG   # make it available inside the stub

cat > "$STUB_BIN/aws" << 'EOF'
#!/usr/bin/env bash
# Append all arguments to the call log
echo "aws $*" >> "$CALL_LOG"
# Return fake S3 output
echo "upload: ./report.pdf to s3://my-bucket/report.pdf"
exit 0
EOF
chmod +x "$STUB_BIN/aws"

# Simulate the script under test calling aws s3 cp
aws s3 cp report.pdf s3://my-bucket/report.pdf
aws s3 cp logs.tar.gz s3://my-bucket/logs.tar.gz

# Verify calls were made with expected arguments
echo "--- Recorded calls ---"
cat "$CALL_LOG"
grep -q 's3://my-bucket/report.pdf' "$CALL_LOG" && echo "PASS: S3 upload verified"

Simuler plusieurs commandes à la fois

Un script réel appelle souvent plusieurs outils externes. Vous pouvez tous les simuler dans le même répertoire STUB_BIN. Chaque fichier de simulacre est indépendant et peut renvoyer des sorties et des codes de sortie différents.

Gardez les simulacres minimaux : renvoyez uniquement ce que le script soumis à l'essai analyse réellement. N'essayez pas de simuler toutes les options ; limitez-vous au sous-ensemble utilisé par votre script.

#!/usr/bin/env bash
# Stub both 'git' and 'docker' for a release script test

STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"

# Stub git: pretend we are on tag v2.1.0
cat > "$STUB_BIN/git" << 'EOF'
#!/usr/bin/env bash
case "$*" in
  *"describe --tags"*) echo "v2.1.0" ;;
  *"rev-parse HEAD"*)  echo "abc1234" ;;
  *) echo "[git stub] unhandled: $*" >&2 ; exit 1 ;;
esac
EOF
chmod +x "$STUB_BIN/git"

# Stub docker: pretend build and push succeed
cat > "$STUB_BIN/docker" << 'EOF'
#!/usr/bin/env bash
echo "[docker stub] $*"
exit 0
EOF
chmod +x "$STUB_BIN/docker"

# Simulate the release logic
VERSION=$(git describe --tags)
SHA=$(git rev-parse HEAD)
echo "Building image for version=$VERSION sha=$SHA"
docker build -t "myapp:$VERSION" .
docker push "myapp:$VERSION"

Utiliser des fonctions comme simulacres (sans fichiers)

Dans les cas simples, vous n'avez pas besoin d'écrire de fichiers. Vous pouvez définir une fonction shell portant le même nom que la commande. Comme les fonctions sont recherchées avant la recherche externe dans PATH, elles sont automatiquement prioritaires.

C'est l'approche la plus rapide pour les essais unitaires de scripts chargés avec source. Toutefois, les fonctions simulées ne fonctionnent que dans le même processus shell ; elles ne sont pas visibles par les sous-shells lancés avec bash -c explicitement ni par les processus exécutés en arrière-plan. Dans ces cas, utilisez l'approche fondée sur des fichiers.

#!/usr/bin/env bash
# Source the script under test (a small helper library)
source_under_test() {
  # Inline the logic we want to test
  get_instance_id() {
    # Would normally call: curl http://169.254.169.254/latest/meta-data/instance-id
    curl -sf http://169.254.169.254/latest/meta-data/instance-id
  }
}
source_under_test

# Override curl with a shell function stub
curl() {
  echo "i-0abc123def456"
  return 0
}
# Export is NOT needed — function is visible in same shell

# Run the function under test
result=$(get_instance_id)
[[ "$result" == "i-0abc123def456" ]] && echo "PASS: instance ID returned" || echo "FAIL"

Exporter des fonctions vers les sous-shells

Lorsque votre script soumis à l'essai lance un sous-shell (par exemple bash script.sh ou une chaîne de traitement), les fonctions shell simulées définies dans le processus parent ne sont pas héritées par défaut. Deux possibilités s'offrent à vous :

  • Utiliser export -f function_name pour exporter la fonction ; elle devient disponible dans les processus bash enfants
  • Ou revenir à des simulacres fondés sur des fichiers dans un répertoire STUB_BIN, qui fonctionnent toujours d'un processus à l'autre

export -f est élégant, mais ne fonctionne qu'avec bash (pas avec sh ni avec d'autres shells). Préférez les simulacres fondés sur des fichiers dans les environnements d'intégration continue multilingues.

#!/usr/bin/env bash
# Demonstrate export -f for subshell-visible function stubs

# Define the stub in the current shell
curl() {
  echo '{"status":"ok"}'
  return 0
}
# Export the function so child bash processes inherit it
export -f curl

# Verify the stub works in a subshell
bash -c '
  response=$(curl -sf http://api.example.com/status)
  echo "Subshell got: $response"
'

# Without export -f, the subshell would call the real curl
# (or fail if curl is not installed)

Intégrer les simulacres à un cadre de test (BATS)

Lorsque vous utilisez BATS (système automatisé d'essais Bash), la préparation des simulacres doit se faire dans le point d'accroche setup(), et le nettoyage dans teardown(). BATS réinitialise l'environnement entre les essais ; chaque essai dispose donc d'un répertoire de simulacres vierge.

Les variables BATS telles que $BATS_TEST_TMPDIR vous fournissent automatiquement un répertoire temporaire propre à chaque essai ; utilisez-le plutôt que mktemp -d pour obtenir un code plus clair.

#!/usr/bin/env bats
# File: test_deploy.bats
# Run with: bats test_deploy.bats

setup() {
  # BATS provides a unique tmpdir per test
  export STUB_BIN="$BATS_TEST_TMPDIR/stub_bin"
  mkdir -p "$STUB_BIN"
  export PATH="$STUB_BIN:$PATH"

  # Default stub: healthy service
  cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
echo '{"healthy":true}'
EOF
  chmod +x "$STUB_BIN/curl"
}

teardown() {
  # BATS auto-removes BATS_TEST_TMPDIR, but explicit is safer
  rm -rf "$STUB_BIN"
}

@test "deploy succeeds when service is healthy" {
  run bash deploy.sh
  [ "$status" -eq 0 ]
  [[ "$output" == *"Deploy complete"* ]]
}

@test "deploy aborts when service is down" {
  # Override the stub for this specific test
  echo -e '#!/usr/bin/env bash\nexit 1' > "$STUB_BIN/curl"
  chmod +x "$STUB_BIN/curl"

  run bash deploy.sh
  [ "$status" -ne 0 ]
}

Vérification des connaissances : simuler des commandes dans Bash

Vérifiez votre compréhension de la simulation de commandes et des modèles de simulacres dans Bash.

Un développeur écrit un essai qui définit une fonction shell nommée aws pour simuler le véritable AWS CLI. L'essai fonctionne correctement lorsqu'il est exécuté directement dans le terminal, mais lorsque la chaîne de traitement d'intégration continue exécute le script soumis à l'essai avec bash deploy.sh, le simulacre est ignoré et la véritable commande aws est appelée.

Quelle est la correction appropriée ?

Récapitulatif : simuler des commandes et des outils externes

Vous avez appris l'ensemble des outils permettant de remplacer de vraies commandes par des imitations contrôlées lors des essais de scripts Bash.

Techniques essentielles étudiées :

  • Ajouter un préfixe à PATH — créer un répertoire STUB_BIN avec mktemp -d, y écrire des fichiers de simulacre exécutables, puis ajouter ce répertoire au début de PATH
  • Codes de sortie des simulacres — renvoyer 0 en cas de réussite et des valeurs différentes de zéro pour simuler des échecs précis (erreurs réseau, accès refusé, etc.)
  • Simulacres espions — ajouter les arguments à un fichier journal dans le simulacre afin de vérifier comment votre script a appelé les outils externes
  • Simulacres multiples — placer plusieurs fichiers de simulacre dans le même STUB_BIN pour simuler tout un écosystème de dépendances à la fois
  • Simulacres sous forme de fonctions — définir une fonction shell portant le même nom qu'une commande pour une simulation dans le même processus ; utiliser export -f pour atteindre les processus bash enfants
  • Intégration à BATS — utiliser les points d'accroche setup()/teardown() et $BATS_TEST_TMPDIR pour une isolation propre de chaque essai

Utilisez toujours un trap '...' EXIT pour garantir le nettoyage des simulacres, quel que soit le résultat de l'essai. Gardez les simulacres minimaux : renvoyez uniquement ce que votre script analyse réellement. Les simulacres fondés sur des fichiers sont le choix le plus portable pour les chaînes de traitement d'intégration continue.

Questions Fréquemment Posées

La leçon « Simuler des commandes et créer des doublures d’outils externes » est-elle gratuite ?

Oui — le texte complet de « Simuler des commandes et créer des doublures d’outils externes » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours DevOps Bootcamp, passe à CoddyKit PRO. Le cours DevOps Bootcamp comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Simuler des commandes et créer des doublures d’outils externes » ?

Redéfinissez PATH et créez de faux binaires pour tester les scripts sans toucher aux systèmes réels. Tu pratiques DevOps Bootcamp avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer DevOps Bootcamp ?

Aucune expérience préalable n'est requise. DevOps Bootcamp sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.

Combien de temps prend la leçon « Simuler des commandes et créer des doublures d’outils externes » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon DevOps Bootcamp ?

Oui. Chaque leçon DevOps Bootcamp inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Tester les fonctions avec Bats-core
  2. Simuler des commandes et créer des doublures d’outils externes
  3. Dispositifs de test, environnements temporaires et couverture
  4. Exécuter des tests Shell dans les pipelines CI
← Retour à DevOps Bootcamp