Funktionen mit Bats-core per Unit-Test prüfen
Strukturieren Sie Testdateien, Assertions sowie Setup und Teardown, um einzelne Bash-Funktionen zu überprüfen.
Funktionen mit Bats-core per Unit-Test prüfen ist eine kostenlose Linux Command Line & Bash Scripting Mastery-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Linux Command Line & Bash Scripting Mastery-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Linux Command Line & Bash Scripting Mastery-Kurs umfasst insgesamt 4 Lektionen.
Was ist Bats-core und warum sollte man es verwenden?
Bats-core (Bash Automated Testing System) ist das De-facto-Unit-Test-Framework für Bash. Damit können Sie strukturierte, reproduzierbare Tests für Ihre Shell-Funktionen und Skripte schreiben – genauso, wie Sie JUnit für Java oder pytest für Python verwenden würden.
- Jeder Test ist ein
@test-Block mit einer verständlichen Beschreibung. - Tests sind erfolgreich, wenn jeder darin ausgeführte Befehl den Exit-Code
0zurückgibt. - Tests schlagen beim ersten Exit-Code ungleich null oder bei einer fehlgeschlagenen Assertion fehl.
- Die Ausgabe ist TAP-kompatibel, sodass CI-Systeme (GitHub Actions, Jenkins, GitLab CI) sie nativ verstehen.
Installieren Sie Bats über Ihren Paketmanager oder klonen Sie das Repository:
# 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.yIhre erste Bats-Testdatei
Eine Bats-Testdatei hat die Erweiterung .bats und beginnt mit einem speziellen Shebang. Der grundlegende Baustein ist die Direktive @test, gefolgt von einer Beschreibung und einem Befehlsblock.
- Der Shebang
#!/usr/bin/env batsteilt der Shell mit, wie die Datei ausgeführt werden soll. - Jeder
@test-Block ist ein unabhängiger Testfall. - Sie können eine einzelne Datei mit
bats my_tests.batsoder ein ganzes Verzeichnis mitbats test/ausführen.
Im Folgenden sehen Sie die minimale Struktur einer Bats-Testdatei:
#!/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
}Die zu testende Funktion mit „load“ laden
In realen Projekten befinden sich Ihre Bash-Funktionen in Bibliotheksdateien und nicht direkt in der Testdatei. Bats stellt den Helfer load bereit, um externe Dateien relativ zum Verzeichnis der Testdatei zu laden.
load '../lib/math.sh'lädt die Datei, bevor jeder Test ausgeführt wird.- Nach dem Laden stehen alle in dieser Datei definierten Funktionen in Ihren Testblöcken zur Verfügung.
- Bewahren Sie Ihre Bibliotheksfunktionen zur sauberen Trennung in einem Verzeichnis
lib/und die Tests in einem Verzeichnistest/auf.
Beispiel für die Projektstruktur und den zugehörigen Test:
# 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" ]
}Grundlegende Assertions: run, $status, $output
Der Befehl run bildet das Herzstück des Testens mit Bats. Anstatt einen Befehl direkt auszuführen, verpacken Sie ihn in run. Dadurch werden Exit-Code und Ausgabe erfasst, ohne dass der Test sofort fehlschlägt.
$status– enthält den Exit-Code des letztenrun-Befehls.$output– enthält die zusammengeführte stdout-Ausgabe des letztenrun-Befehls.$lines– ein Array, dessen Elemente jeweils eine Ausgabezeile enthalten (${lines[0]},${lines[1]}usw.).
So können Sie sowohl Erfolgs- als auch Fehlerfälle prüfen:
#!/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"* ]]
}Ausdrucksstarke Assertions mit bats-assert verwenden
Die integrierten [ ]-Assertions funktionieren, liefern aber wenig hilfreiche Fehlermeldungen. Die Hilfsbibliothek bats-assert stellt ausdrucksstarke Assertions bereit, die genau ausgeben, was schiefgelaufen ist.
assert_success– prüft, ob$statusgleich 0 ist.assert_failure– prüft, ob$statusungleich null ist.assert_output– prüft, ob$outputder angegebenen Zeichenkette entspricht.assert_output --partial– prüft, ob die Ausgabe das Teilstück enthält.refute_output --partial– prüft, ob die Ausgabe das Teilstück NICHT enthält.
Installieren Sie die Bibliothek, indem Sie bats-core/bats-assert in einen Ordner test/helpers/ klonen, und laden Sie sie anschließend:
#!/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 und teardown: Hooks im Testlebenszyklus
Bats stellt zwei spezielle Funktionen bereit – setup und teardown –, die automatisch um jeden Test herum ausgeführt werden. Verwenden Sie sie, um gemeinsam genutzten Zustand vorzubereiten und aufzuräumen, damit jeder Test in einer bekannten Umgebung startet.
setup()wird vor jedem einzelnen@test-Block ausgeführt.teardown()wird nach jedem einzelnen@test-Block ausgeführt, auch wenn der Test fehlschlägt.- Typische Einsatzbereiche sind das Erstellen temporärer Verzeichnisse, das Setzen von Umgebungsvariablen und das Entfernen temporärer Dateien nach dem 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 und teardown_file: Hooks auf Suite-Ebene
Manchmal müssen aufwendige Ressourcen nur einmal pro Datei eingerichtet werden – nicht vor jedem einzelnen Test. Dafür stellt Bats setup_file und teardown_file bereit.
setup_file()wird einmal vor allen Tests in der Datei ausgeführt.teardown_file()wird einmal nach allen Tests in der Datei ausgeführt.- Verwenden Sie
BATS_FILE_TMPDIR(automatisch verfügbar), um Daten zwischensetup_fileund Ihren Tests gemeinsam zu nutzen – normale Variablen bleiben über Subshells hinweg nicht erhalten.
Typischer Anwendungsfall: Einen Mock-Server starten oder einmalig eine Binärdatei erstellen und am Ende wieder aufräumen:
#!/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"
}Funktionen testen, die Dateien ändern
Ein sehr häufiges Muster ist das Testen von Bash-Funktionen, die aus dem Dateisystem lesen oder darin schreiben. Die wichtigste Technik besteht darin, temporäre Verzeichnisse zu verwenden (über mktemp -d in setup), damit Tests niemals echte Dateien berühren und sich nicht gegenseitig beeinflussen.
- Arbeiten Sie immer innerhalb von
$TEST_DIR(oder$BATS_TEST_TMPDIR– in aktuellen Bats-Versionen automatisch verfügbar). - Verwenden Sie die Hilfsbibliothek
bats-filefür übersichtliche Datei-Assertions wieassert_file_existsundassert_file_contains. - Verwenden Sie niemals fest codierte Pfade wie
/tmp/myfile– parallele Testläufe würden miteinander kollidieren.
#!/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"
}Externe Befehle mocken
Funktionen rufen häufig externe Programme wie curl, aws oder git auf. In Unit-Tests möchten Sie Ihre Logik testen, nicht den tatsächlichen externen Befehl. Die sauberste Mocking-Technik in Bats besteht darin, in setup eine Shell-Funktion mit demselben Namen wie der Befehl zu definieren – sie hat Vorrang vor der echten Binärdatei.
- Definieren Sie in
setupeine Funktion wiecurl() { echo 'mocked response'; return 0; }und exportieren Sie sie. - Verwenden Sie
export -f curl, damit die Funktion in vonrungestarteten Subshells sichtbar ist. - Für komplexere Szenarien können Sie den Mock auch in eine temporäre Datei im
PATHschreiben.
#!/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"
}Tests überspringen und mit Tags versehen
Nicht jeder Test kann jederzeit ausgeführt werden – manchmal benötigen Sie eine echte Netzwerkverbindung, ein bestimmtes Tool oder ein bestimmtes Betriebssystem. Bats stellt skip bereit, um einen Test bedingt und mit einer informativen Nachricht zu überspringen, anstatt ihn auszukommentieren oder die Testsuite zu unterbrechen.
- Rufen Sie
skip "reason"an beliebiger Stelle innerhalb eines@test-Blocks auf, um den Test zu überspringen. - Übersprungene Tests werden in der Ausgabe als
Sangezeigt und zählen nicht als Fehler. - Bats 1.5+ unterstützt Tags: Kennzeichnen Sie Tests mit
# bats test_tags=slow,networkund filtern Sie sie mitbats --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/Eine vollständige Testsuite strukturieren
Ein gut organisiertes Bats-Projekt folgt einer vorhersehbaren Verzeichnisstruktur. Dadurch lassen sich neue Mitwirkende leichter einarbeiten und CI-Pipelines einfacher integrieren.
Empfohlene Struktur:
lib/– produktive Bash-Funktionen (eine Datei pro Aufgabenbereich:math.sh,fileutils.sh).test/– eine.bats-Datei pro Bibliotheksdatei (math.bats,fileutils.bats).test/helpers/– bats-assert, bats-file und bats-support als Git-Submodule.Makefile– eintest-Target, sodass Mitwirkende einfachmake testausführen können.
Führen Sie die gesamte Testsuite mit einem Befehl aus:
# 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
# `-- MakefileWissenscheck: Bats-core-Assertions
Testen Sie Ihr Verständnis des grundlegenden Testmechanismus von Bats-core.
Zusammenfassung: Bash mit Bats-core per Unit-Tests testen
In dieser Lektion haben Sie gelernt, wie Sie mit Bats-core Unit-Tests für einzelne Bash-Funktionen strukturieren und schreiben. Hier sind die wichtigsten Erkenntnisse:
- Struktur der Testdatei – Verwenden Sie den Shebang
#!/usr/bin/env batsund@test-Blöcke mit aussagekräftigen Namen. load– Laden Sie Ihre Bibliotheksdateien, damit Funktionen in Tests verfügbar sind, ohne sie per Copy-and-paste zu duplizieren.run+$status+$output– das zentrale Trio; verwenden Sie immerrun, um Ergebnisse zu erfassen, ohne den Test sofort fehlschlagen zu lassen.- bats-assert – Verwenden Sie vorzugsweise
assert_success,assert_failureundassert_outputanstelle von einfachen[ ]-Assertions, um verständliche Fehlermeldungen zu erhalten. setup/teardown– werden vor bzw. nach jedem Test ausgeführt;setup_file/teardown_filewerden einmal pro Datei ausgeführt.- Mocking – Überschreiben Sie externe Befehle mit gleichnamigen Shell-Funktionen, die mit
export -fexportiert werden. skip– Überspringen Sie bedingt Tests, die auf nicht verfügbaren Ressourcen beruhen.- Projektstruktur – Halten Sie
lib/,test/undtest/helpers/zur besseren Wartbarkeit und CI-Integration getrennt.
Mit diesen Mustern verleihen Sie Ihren Bash-Projekten dieselbe Testdisziplin, die Sie auf jedes moderne Softwareprojekt anwenden würden.
Häufig gestellte Fragen
Ist die Lektion „Funktionen mit Bats-core per Unit-Test prüfen“ kostenlos?
Ja — der vollständige Text von „Funktionen mit Bats-core per Unit-Test prüfen“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Linux Command Line & Bash Scripting Mastery-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Linux Command Line & Bash Scripting Mastery-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Funktionen mit Bats-core per Unit-Test prüfen“?
Strukturieren Sie Testdateien, Assertions sowie Setup und Teardown, um einzelne Bash-Funktionen zu überprüfen. Du übst Linux Command Line & Bash Scripting Mastery mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Linux Command Line & Bash Scripting Mastery zu starten?
Keine Vorkenntnisse erforderlich. Linux Command Line & Bash Scripting Mastery auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.
Wie lange dauert die Lektion „Funktionen mit Bats-core per Unit-Test prüfen“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Linux Command Line & Bash Scripting Mastery-Lektion Code schreiben und ausführen?
Ja. Jede Linux Command Line & Bash Scripting Mastery-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Funktionen mit Bats-core per Unit-Test prüfen
- Befehle mocken und externe Tools stubben
- Fixtures, temporäre Umgebungen und Coverage
- Shell-Tests in CI-Pipelines ausführen