0Pricing
Linux Command Line & Bash Scripting Mastery · Leçon

Tester les fonctions avec Bats-core

Structurez les fichiers de test, les assertions et la préparation et le nettoyage afin de vérifier les fonctions Bash individuellement.

Tester les fonctions avec Bats-core est une leçon Linux Command Line & Bash Scripting Mastery gratuite sur CoddyKit. Ceci est la leçon 1 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 Linux Command Line & Bash Scripting Mastery, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Linux Command Line & Bash Scripting Mastery comprend 4 leçons au total.

Qu’est-ce que Bats-core et pourquoi l’utiliser ?

Bats-core (Bash Automated Testing System) est le framework de référence pour les tests unitaires de Bash. Il vous permet d’écrire des tests structurés et reproductibles pour vos fonctions et scripts shell, de la même manière que vous utiliseriez JUnit pour Java ou pytest pour Python.

  • Chaque test est un bloc @test accompagné d’une description compréhensible.
  • Les tests réussissent lorsque chaque commande qu’ils contiennent renvoie le code de sortie 0.
  • Les tests échouent dès qu’un code de sortie différent de zéro est renvoyé ou qu’une assertion échoue.
  • La sortie est compatible avec TAP : les systèmes d’intégration continue (GitHub Actions, Jenkins, GitLab CI) la comprennent nativement.

Installez-le avec votre gestionnaire de paquets ou clonez le dépôt :

# Install via git (recommended — always latest)
git clone https://github.com/bats-core/bats-core.git
cd bats-core && sudo ./install.sh /usr/local

# Or on macOS with Homebrew
brew install bats-core

# Verify installation
bats --version
# bats 1.x.y

Votre premier fichier de test Bats

Un fichier de test Bats porte l’extension .bats et commence par un shebang spécial. L’élément de base est la directive @test, suivie d’une chaîne de description et d’un bloc de commandes.

  • Le shebang #!/usr/bin/env bats indique au shell comment exécuter le fichier.
  • Chaque bloc @test constitue un cas de test indépendant.
  • Vous pouvez exécuter un seul fichier avec bats my_tests.bats ou un répertoire entier avec bats test/.

Voici la structure minimale d’un fichier de test Bats :

#!/usr/bin/env bats
# File: test/hello.bats

@test "echo outputs the expected string" {
  result=$(echo "hello world")
  [ "$result" = "hello world" ]
}

@test "false command causes test to fail" {
  # Uncommenting the next line would make this test fail:
  # false
  true
}

Charger la fonction testée avec « load »

Dans les projets réels, vos fonctions Bash se trouvent dans des fichiers de bibliothèque, et non directement dans le fichier de test. Bats fournit l’assistant load, qui permet de charger des fichiers externes relativement au répertoire du fichier de test.

  • load '../lib/math.sh' charge le fichier avant l’exécution de chaque test.
  • Après le chargement, toutes les fonctions définies dans ce fichier sont disponibles dans vos blocs de test.
  • Conservez vos fonctions de bibliothèque dans un répertoire lib/ et vos tests dans un répertoire test/ afin de bien les séparer.

Exemple de structure de projet et du test correspondant :

# Project layout:
# lib/math.sh       <- functions to test
# test/math.bats    <- test file

# lib/math.sh
add() {
  echo $(( $1 + $2 ))
}

divide() {
  if [ "$2" -eq 0 ]; then
    echo "Error: division by zero" >&2
    return 1
  fi
  echo $(( $1 / $2 ))
}

# test/math.bats
#!/usr/bin/env bats

load '../lib/math.sh'

@test "add returns correct sum" {
  result=$(add 3 4)
  [ "$result" = "7" ]
}

Assertions essentielles : run, $status, $output

La commande run est au cœur des tests Bats. Au lieu d’exécuter directement une commande, vous pouvez l’entourer de run pour capturer son code de sortie et sa sortie, sans provoquer l’échec immédiat du test.

  • $status — contient le code de sortie de la dernière commande run.
  • $output — contient la sortie standard combinée de la dernière commande run.
  • $lines — tableau dont chaque élément correspond à une ligne de sortie (${lines[0]}, ${lines[1]}, etc.).

Vous pouvez ainsi vérifier les cas de réussite comme les cas d’échec :

#!/usr/bin/env bats

load '../lib/math.sh'

@test "divide 10 by 2 returns 5" {
  run divide 10 2
  [ "$status" -eq 0 ]
  [ "$output" = "5" ]
}

@test "divide by zero returns exit code 1" {
  run divide 10 0
  [ "$status" -eq 1 ]
}

@test "divide by zero prints error message" {
  run divide 10 0
  # $output captures stderr too when redirected inside the function
  [[ "$output" == *"division by zero"* ]]
}

Utiliser bats-assert pour des assertions explicites

Les assertions intégrées [ ] fonctionnent, mais leurs messages d’échec sont peu instructifs. La bibliothèque d’assistants bats-assert fournit des fonctions d’assertion explicites qui indiquent précisément ce qui s’est mal passé.

  • assert_success — vérifie que $status vaut 0.
  • assert_failure — vérifie que $status est différent de zéro.
  • assert_output — vérifie que $output est égal à la chaîne fournie.
  • assert_output --partial — vérifie que la sortie contient la sous-chaîne.
  • refute_output --partial — vérifie que la sortie ne contient PAS la sous-chaîne.

Installez-la en clonant bats-core/bats-assert dans un répertoire test/helpers/, puis chargez-la :

#!/usr/bin/env bats

# Load bats-assert (cloned into test/helpers/bats-assert)
load 'helpers/bats-assert/load'
load '../lib/math.sh'

@test "add 5 and 3 gives 8" {
  run add 5 3
  assert_success
  assert_output "8"
}

@test "divide by zero fails with descriptive message" {
  run divide 9 0
  assert_failure
  assert_output --partial "division by zero"
}

@test "add does not output an error" {
  run add 1 1
  refute_output --partial "Error"
}

setup et teardown : points d’accroche du cycle de vie des tests

Bats fournit deux fonctions spéciales — setup et teardown — qui s’exécutent automatiquement autour de chaque test. Utilisez-les pour préparer et nettoyer l’état partagé, afin que chaque test démarre dans un environnement connu.

  • setup() s’exécute avant chaque bloc @test.
  • teardown() s’exécute après chaque bloc @test, même si le test échoue.
  • Utilisations courantes : créer des répertoires temporaires, définir des variables d’environnement et supprimer les fichiers temporaires après le test.
#!/usr/bin/env bats

load '../lib/fileutils.sh'

setup() {
  # Create a fresh temp directory before every test
  TEST_DIR=$(mktemp -d)
  export TEST_DIR
}

teardown() {
  # Always clean up, even on test failure
  rm -rf "$TEST_DIR"
}

@test "write_file creates a file with correct content" {
  run write_file "$TEST_DIR/hello.txt" "hello world"
  assert_success
  [ -f "$TEST_DIR/hello.txt" ]
  [ "$(cat "$TEST_DIR/hello.txt")" = "hello world" ]
}

@test "write_file fails when directory does not exist" {
  run write_file "/nonexistent/dir/file.txt" "data"
  assert_failure
}

setup_file et teardown_file : points d’accroche au niveau de la suite

Il suffit parfois de configurer des ressources coûteuses une seule fois par fichier, et non avant chaque test. Bats fournit setup_file et teardown_file à cette fin.

  • setup_file() s’exécute une fois avant tous les tests du fichier.
  • teardown_file() s’exécute une fois après tous les tests du fichier.
  • Utilisez BATS_FILE_TMPDIR (disponible automatiquement) pour partager des données entre setup_file et vos tests : les variables ordinaires ne sont pas conservées entre les sous-shells.

Cas d’utilisation courant : démarrer un serveur simulé ou compiler un binaire une seule fois, puis l’arrêter à la fin :

#!/usr/bin/env bats

setup_file() {
  # Build the project binary once for all tests in this file
  make build --silent
  export BINARY="$PWD/bin/myapp"
  echo "Binary built: $BINARY"
}

teardown_file() {
  # Remove the binary after all tests complete
  rm -f "$BINARY"
  echo "Cleaned up binary"
}

setup() {
  # Still runs before each individual test
  TEST_TMP=$(mktemp -d)
}

teardown() {
  rm -rf "$TEST_TMP"
}

@test "myapp --version outputs version string" {
  run "$BINARY" --version
  assert_output --partial "1.0"
}

Tester les fonctions qui modifient des fichiers

Il est très courant de tester des fonctions Bash qui lisent ou écrivent dans le système de fichiers. La technique essentielle consiste à utiliser des répertoires temporaires (avec mktemp -d dans setup) afin que les tests ne touchent jamais aux vrais fichiers et n’interfèrent jamais les uns avec les autres.

  • Travaillez toujours dans $TEST_DIR (ou $BATS_TEST_TMPDIR, disponible automatiquement dans les versions récentes de Bats).
  • Utilisez la bibliothèque d’assistants bats-file pour effectuer simplement des vérifications sur les fichiers, comme assert_file_exists et assert_file_contains.
  • Ne codez jamais en dur des chemins comme /tmp/myfile : les exécutions parallèles des tests entreraient en conflit.
#!/usr/bin/env bats

load 'helpers/bats-assert/load'
load 'helpers/bats-file/load'
load '../lib/fileutils.sh'

setup() {
  TEST_DIR="$BATS_TEST_TMPDIR"
}

# lib/fileutils.sh defines:
# append_line() { echo "$2" >> "$1"; }

@test "append_line adds a line to an existing file" {
  echo "first line" > "$TEST_DIR/log.txt"

  run append_line "$TEST_DIR/log.txt" "second line"
  assert_success

  assert_file_contains "$TEST_DIR/log.txt" "second line"
}

@test "append_line creates file if it does not exist" {
  run append_line "$TEST_DIR/new.txt" "hello"
  assert_success
  assert_file_exists "$TEST_DIR/new.txt"
}

Simuler des commandes externes

Les fonctions appellent souvent des programmes externes comme curl, aws ou git. Dans les tests unitaires, vous voulez tester votre logique, et non la véritable commande externe. La technique de simulation la plus simple avec Bats consiste à définir dans setup une fonction shell portant le même nom que la commande : elle prend le pas sur le véritable binaire.

  • Définissez dans setup une fonction telle que curl() { echo 'mocked response'; return 0; }, puis exportez-la.
  • Utilisez export -f curl pour que la fonction soit visible dans les sous-shells créés par run.
  • Vous pouvez également écrire la simulation dans un fichier temporaire placé dans le PATH pour les scénarios plus complexes.
#!/usr/bin/env bats

load 'helpers/bats-assert/load'
load '../lib/network.sh'

# lib/network.sh defines:
# fetch_status() {
#   local url="$1"
#   local code
#   code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
#   echo "$code"
# }

setup() {
  # Override 'curl' with a mock function
  curl() {
    # Simulate a 200 OK response
    echo "200"
    return 0
  }
  export -f curl
}

@test "fetch_status returns 200 when curl reports 200" {
  run fetch_status "https://example.com"
  assert_success
  assert_output "200"
}

Ignorer et étiqueter les tests

Il n’est pas toujours possible d’exécuter tous les tests : vous avez parfois besoin d’une véritable connexion réseau, d’un outil précis ou d’un système d’exploitation particulier. Bats fournit skip pour ignorer conditionnellement un test avec un message explicite, plutôt que de le commenter ou de provoquer l’échec de la suite.

  • Appelez skip "reason" n’importe où dans un bloc @test pour ignorer ce test.
  • Les tests ignorés apparaissent dans la sortie sous la forme S et ne sont pas comptabilisés comme des échecs.
  • Bats 1.5+ prend en charge les étiquettes : annotez les tests avec # bats test_tags=slow,network et filtrez-les avec bats --filter-tags network test/.
#!/usr/bin/env bats

# bats test_tags=network
@test "API returns valid JSON" {
  # Skip if no internet connectivity
  if ! ping -c1 -W1 8.8.8.8 &>/dev/null; then
    skip "No network connection available"
  fi

  run curl -s "https://api.example.com/health"
  assert_success
  assert_output --partial '"status"'
}

# bats test_tags=unit
@test "slug function lowercases and replaces spaces" {
  # Always runs — pure function, no external deps
  slug() { echo "$1" | tr '[:upper:]' '[:lower:]' | tr ' ' '-'; }
  run slug "Hello World"
  assert_output "hello-world"
}

# Run only unit tests:
# bats --filter-tags unit test/

Structurer une suite de tests complète

Un projet Bats bien organisé suit une structure de répertoires prévisible, ce qui facilite l’intégration des nouveaux contributeurs et l’intégration aux chaînes d’intégration continue.

Structure recommandée :

  • lib/ — fonctions Bash de production (un fichier par fonctionnalité : math.sh, fileutils.sh).
  • test/ — un fichier .bats par fichier de bibliothèque (math.bats, fileutils.bats).
  • test/helpers/ — bats-assert, bats-file et bats-support en tant que sous-modules git.
  • Makefile — une cible test pour que les contributeurs n’aient plus qu’à exécuter make test.

Exécutez toute la suite avec une seule commande :

# Makefile
.PHONY: test
test:
	bats test/

# Run all tests recursively (Bats 1.5+)
# bats --recursive test/

# Run a specific file
# bats test/math.bats

# Run with verbose (TAP) output for CI
# bats --tap test/

# Example directory tree:
# .
# |-- lib/
# |   |-- math.sh
# |   `-- fileutils.sh
# |-- test/
# |   |-- helpers/
# |   |   |-- bats-assert/
# |   |   `-- bats-file/
# |   |-- math.bats
# |   `-- fileutils.bats
# `-- Makefile

Vérification des connaissances : assertions Bats-core

Vérifiez votre compréhension du mécanisme central de test de Bats-core.

Récapitulatif : tester Bash avec Bats-core

Dans cette leçon, vous avez appris à structurer et à écrire des tests unitaires pour des fonctions Bash individuelles avec Bats-core. Voici les points essentiels à retenir :

  • Structure des fichiers de test — utilisez le shebang #!/usr/bin/env bats et des blocs @test portant des noms explicites.
  • load — chargez vos fichiers de bibliothèque afin que les fonctions soient disponibles dans les tests, sans copier-coller.
  • run + $status + $output — le trio essentiel ; utilisez toujours run pour capturer les résultats sans provoquer l’échec immédiat du test.
  • bats-assert — préférez assert_success, assert_failure et assert_output aux crochets bruts [ ] pour obtenir des messages d’échec lisibles.
  • setup / teardown — s’exécutent avant et après chaque test ; setup_file / teardown_file s’exécutent une fois par fichier.
  • Simulation — remplacez les commandes externes par des fonctions shell portant le même nom et exportées avec export -f.
  • skip — ignorez conditionnellement les tests qui dépendent de ressources indisponibles.
  • Structure du projet — séparez lib/, test/ et test/helpers/ pour faciliter la maintenance et l’intégration continue.

Ces pratiques donnent à vos projets Bash la même rigueur de test que celle que vous appliqueriez à tout projet logiciel moderne.

Questions Fréquemment Posées

La leçon « Tester les fonctions avec Bats-core » est-elle gratuite ?

Oui — le texte complet de « Tester les fonctions avec Bats-core » 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 Linux Command Line & Bash Scripting Mastery, passe à CoddyKit PRO. Le cours Linux Command Line & Bash Scripting Mastery comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Tester les fonctions avec Bats-core » ?

Structurez les fichiers de test, les assertions et la préparation et le nettoyage afin de vérifier les fonctions Bash individuellement. Tu pratiques Linux Command Line & Bash Scripting Mastery 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 Linux Command Line & Bash Scripting Mastery ?

Aucune expérience préalable n'est requise. Linux Command Line & Bash Scripting Mastery 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 1 sur 4.

Combien de temps prend la leçon « Tester les fonctions avec Bats-core » ?

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 Linux Command Line & Bash Scripting Mastery ?

Oui. Chaque leçon Linux Command Line & Bash Scripting Mastery 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 à Linux Command Line & Bash Scripting Mastery