Functies testen met Bats-core
Structureer testbestanden, assertions en setup/teardown om afzonderlijke Bash-functies te verifiëren.
Functies testen met Bats-core is een gratis De Linux-opdrachtregel en Bash-scripting beheersen-les op CoddyKit. Dit is les 1 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject De Linux-opdrachtregel en Bash-scripting beheersen. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus De Linux-opdrachtregel en Bash-scripting beheersen bevat in totaal 4 lessen.
Wat is Bats-core en waarom zou je het gebruiken?
Bats-core (Bash Automated Testing System) is het de facto framework voor unittests in Bash. Je kunt er gestructureerde, herhaalbare tests mee schrijven voor je shellfuncties en scripts — net zoals je JUnit voor Java of pytest voor Python zou gebruiken.
- Elke test is een
@test-blok met een leesbare beschrijving. - Tests slagen wanneer elke opdracht erin afsluit met afsluitcode
0. - Tests mislukken bij de eerste afsluitcode die niet nul is of bij een mislukte controle.
- De uitvoer is TAP-compatibel, zodat CI-systemen (GitHub Actions, Jenkins, GitLab CI) deze standaard begrijpen.
Installeer het via je pakketbeheerder of kloon de opslagplaats:
# 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.yJe eerste Bats-testbestand
Een Bats-testbestand heeft de extensie .bats en begint met een speciale shebang. Het belangrijkste bouwblok is de instructie @test, gevolgd door een beschrijvingsreeks en een blok opdrachten.
- De shebang
#!/usr/bin/env batsvertelt de shell hoe het bestand moet worden uitgevoerd. - Elk
@test-blok is een onafhankelijke test. - Je kunt één bestand uitvoeren met
bats my_tests.batsof een volledige map metbats test/.
Hieronder zie je de minimale structuur van een Bats-testbestand:
#!/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
}De te testen functie laden met 'load'
In echte projecten staan je Bash-functies in bibliotheekbestanden, niet in het testbestand zelf. Bats biedt de helper load om externe bestanden te laden relatief aan de map van het testbestand.
load '../lib/math.sh'laadt het bestand voordat elke test wordt uitgevoerd.- Na het laden zijn alle functies uit dat bestand beschikbaar in je testblokken.
- Houd je bibliotheekfuncties in een map
lib/en je tests in een maptest/voor een duidelijke scheiding.
Voorbeeld van een projectindeling en de bijbehorende 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" ]
}Basiscontroles: run, $status, $output
De opdracht run vormt het hart van testen met Bats. In plaats van een opdracht rechtstreeks uit te voeren, wikkel je deze in run. Zo leg je de afsluitcode en uitvoer vast zonder dat de test meteen mislukt.
$status— bevat de afsluitcode van de laatste opdracht metrun.$output— bevat de gecombineerde standaarduitvoer van de laatste opdracht metrun.$lines— een array waarvan elk element één uitvoerregel is (${lines[0]},${lines[1]}, enzovoort).
Zo kun je zowel geslaagde als mislukte scenario's controleren:
#!/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"* ]]
}Expressieve controles gebruiken met bats-assert
De ingebouwde controles met [ ] werken, maar leveren weinig informatie bij een mislukking. De helperbibliotheek bats-assert biedt duidelijke controlefuncties die precies aangeven wat er misging.
assert_success— controleert of$status0 is.assert_failure— controleert of$statusniet nul is.assert_output— controleert of$outputgelijk is aan de opgegeven tekenreeks.assert_output --partial— controleert of de uitvoer de deeltekenreeks bevat.refute_output --partial— controleert of de uitvoer de deeltekenreeks NIET bevat.
Installeer de bibliotheek door bats-core/bats-assert naar een map test/helpers/ te klonen en laad deze daarna:
#!/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 en teardown: hooks voor de levenscyclus van tests
Bats biedt twee speciale functies — setup en teardown — die automatisch rond elke test worden uitgevoerd. Gebruik ze om gedeelde toestand voor te bereiden en op te ruimen, zodat elke test met een bekende omgeving begint.
setup()wordt uitgevoerd vóór elk afzonderlijk@test-blok.teardown()wordt uitgevoerd na elk afzonderlijk@test-blok, ook als de test mislukt.- Veelgebruikte toepassingen zijn tijdelijke mappen maken, omgevingsvariabelen instellen en tijdelijke bestanden na de test verwijderen.
#!/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 en teardown_file: hooks op testbundelniveau
Soms hoef je kostbare bronnen maar één keer per bestand in te stellen — niet vóór elke afzonderlijke test. Bats biedt hiervoor setup_file en teardown_file.
setup_file()wordt één keer uitgevoerd vóór alle tests in het bestand.teardown_file()wordt één keer uitgevoerd na alle tests in het bestand.- Gebruik
BATS_FILE_TMPDIR(automatisch beschikbaar) om gegevens te delen tussensetup_fileen je tests — gewone variabelen blijven niet behouden tussen subprocessen.
Een gebruikelijk voorbeeld is één keer een mockserver starten of een binair bestand bouwen en dit aan het einde weer afsluiten:
#!/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"
}Functies testen die bestanden wijzigen
Een veelvoorkomend patroon is het testen van Bash-functies die het bestandssysteem lezen of erin schrijven. De belangrijkste techniek is het gebruik van tijdelijke mappen (via mktemp -d in setup), zodat tests nooit echte bestanden aanraken en elkaar nooit hinderen.
- Werk altijd binnen
$TEST_DIR(of$BATS_TEST_TMPDIR— automatisch beschikbaar in recente versies van Bats). - Gebruik de helperbibliotheek
bats-filevoor duidelijke bestandscontroles, zoalsassert_file_existsenassert_file_contains. - Leg nooit paden zoals
/tmp/myfilehard vast — parallelle tests zullen elkaar dan in de weg zitten.
#!/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 opdrachten nabootsen
Functies roepen vaak externe programma's aan, zoals curl, aws of git. In unittests wil je jouw logica testen, niet de echte externe opdracht. De eenvoudigste techniek voor nabootsen in Bats is een shellfunctie met dezelfde naam als de opdracht definiëren in setup — die krijgt voorrang op het echte binaire bestand.
- Definieer in
setupeen functie zoalscurl() { echo 'mocked response'; return 0; }en exporteer deze. - Gebruik
export -f curlzodat de functie zichtbaar is in subprocessen die doorrunworden gestart. - Je kunt de nabootsing voor complexere scenario's ook naar een tijdelijk bestand op
PATHschrijven.
#!/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 overslaan en labelen
Niet elke test kan altijd worden uitgevoerd — soms heb je een echte netwerkverbinding, een specifiek hulpprogramma of een bepaald besturingssysteem nodig. Bats biedt skip om een test voorwaardelijk over te slaan met een informatieve melding, in plaats van deze uit te commentariëren of de testbundel te laten mislukken.
- Roep
skip "reason"ergens binnen een@test-blok aan om die test over te slaan. - Overgeslagen tests verschijnen in de uitvoer als
Sen tellen niet als mislukkingen. - Bats 1.5+ ondersteunt labels: voorzie tests van
# bats test_tags=slow,networken filter metbats --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/Een volledige testbundel structureren
Een goed georganiseerde Bats-opslagplaats volgt een voorspelbare mappenindeling, waardoor nieuwe bijdragers gemakkelijk kunnen beginnen en integratie met CI-pijplijnen eenvoudig wordt.
Aanbevolen structuur:
lib/— Bash-functies voor productie (één bestand per onderwerp:math.sh,fileutils.sh).test/— één.bats-bestand per bibliotheekbestand (math.bats,fileutils.bats).test/helpers/— bats-assert, bats-file en bats-support als Git-submodules.Makefile— eentest-doel, zodat bijdragers alleenmake testhoeven uit te voeren.
Voer de volledige testbundel uit met één opdracht:
# 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
# `-- MakefileKennischeck: controles in Bats-core
Test je begrip van het belangrijkste testmechanisme van Bats-core.
Samenvatting: Bash-unittests met Bats-core
In deze les heb je geleerd hoe je unittests voor afzonderlijke Bash-functies structureert en schrijft met Bats-core. Dit zijn de belangrijkste punten:
- Structuur van testbestanden — gebruik de shebang
#!/usr/bin/env batsen@test-blokken met beschrijvende namen. load— laad je bibliotheekbestanden, zodat functies in tests beschikbaar zijn zonder code te kopiëren en te plakken.run+$status+$output— het belangrijkste trio; gebruik altijdrunom resultaten vast te leggen zonder de test meteen te laten mislukken.- bats-assert — geef voor leesbare meldingen bij mislukkingen de voorkeur aan
assert_success,assert_failureenassert_outputboven onbewerkte[ ]. setup/teardown— worden vóór en na elke test uitgevoerd;setup_file/teardown_fileworden één keer per bestand uitgevoerd.- Nabootsen — overschaduw externe opdrachten met shellfuncties met dezelfde naam, geëxporteerd met
export -f. skip— sla tests die afhankelijk zijn van niet-beschikbare bronnen voorwaardelijk over.- Projectindeling — houd
lib/,test/entest/helpers/gescheiden voor onderhoudbaarheid en CI-integratie.
Met deze patronen geef je je Bash-projecten dezelfde testdiscipline die je op elk modern softwareproject zou toepassen.
Leer Bash met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 22
- Lessen
- 88
Veelgestelde vragen
Is de les “Functies testen met Bats-core” gratis?
Ja — de volledige tekst van “Functies testen met Bats-core” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus De Linux-opdrachtregel en Bash-scripting beheersen wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus De Linux-opdrachtregel en Bash-scripting beheersen bevat in totaal 4 lessen.
Wat leer ik in “Functies testen met Bats-core”?
Structureer testbestanden, assertions en setup/teardown om afzonderlijke Bash-functies te verifiëren. Je oefent met De Linux-opdrachtregel en Bash-scripting beheersen door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met De Linux-opdrachtregel en Bash-scripting beheersen te beginnen?
Ervaring vooraf is niet nodig. De Linux-opdrachtregel en Bash-scripting beheersen op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 1 van 4.
Hoe lang duurt de les “Functies testen met Bats-core”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over De Linux-opdrachtregel en Bash-scripting beheersen?
Ja. Elke les over De Linux-opdrachtregel en Bash-scripting beheersen bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Functies testen met Bats-core
- Commando's mocken en externe tools stubben
- Fixtures, tijdelijke omgevingen en coverage
- Shelltests uitvoeren in CI-pipelines