Enhetstesta funktioner med Bats-core
Strukturera testfiler, assertioner och setup/teardown för att verifiera enskilda Bash-funktioner.
Enhetstesta funktioner med Bats-core är en gratis lektion i DevOps-bootcamp på CoddyKit. Detta är lektion 1 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för DevOps-bootcamp, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i DevOps-bootcamp innehåller totalt 4 lektioner.
Vad är Bats-core och varför använda det?
Bats-core (Bash Automated Testing System) är det de facto-standardiserade ramverket för enhetstestning av Bash. Det låter Er skriva strukturerade, repeterbara tester för Era shellfunktioner och skript — på samma sätt som Ni skulle använda JUnit för Java eller pytest för Python.
- Varje test är ett
@test-block med en beskrivning som är lätt att förstå. - Tester lyckas när varje kommando inuti returnerar slutstatus
0. - Tester misslyckas vid den första slutstatus som inte är noll eller vid ett misslyckat påstående.
- Utdata är TAP-kompatibel, så CI-system (GitHub Actions, Jenkins, GitLab CI) förstår den direkt.
Installera via Er pakethanterare eller klona arkivet:
# 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.yErt första Bats-testfil
En Bats-testfil har filändelsen .bats och börjar med en särskild shebang-rad. Det centrala byggblocket är direktivet @test, följt av en beskrivande sträng och ett block med kommandon.
- Shebang-raden
#!/usr/bin/env batstalar om för shellen hur filen ska köras. - Varje
@test-block är ett fristående testfall. - Ni kan köra en enskild fil med
bats my_tests.batseller en hel katalog medbats test/.
Nedan visas den minimala strukturen för en Bats-testfil:
#!/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
}Läsa in funktionen som testas med 'load'
I verkliga projekt ligger Era Bash-funktioner i biblioteksfiler, inte i själva testfilen. Bats tillhandahåller hjälpfunktionen load för att läsa in externa filer relativt till testfilens katalog.
load '../lib/math.sh'läser in filen innan varje test körs.- Efter inläsningen är alla funktioner som definieras i filen tillgängliga i Era testblock.
- Håll Era biblioteksfunktioner i en
lib/-katalog och Era tester i entest/-katalog för en tydlig uppdelning.
Exempel på projektlayout och tillhörande 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" ]
}Centrala påståenden: run, $status, $output
Kommandot run är kärnan i Bats-testning. I stället för att köra ett kommando direkt omsluter Ni det med run, vilket fångar dess slutstatus och utdata utan att testet omedelbart misslyckas.
$status— innehåller slutstatusen för det senasterun-kommandot.$output— innehåller den sammanlagda standardutmatningen från det senasterun-kommandot.$lines— en array där varje element är en rad utdata (${lines[0]},${lines[1]}osv.).
Detta låter Er kontrollera både lyckade och misslyckade fall:
#!/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"* ]]
}Använda bats-assert för tydliga påståenden
De inbyggda påståendena med [ ] fungerar, men ger bristfälliga felmeddelanden. Hjälpbiblioteket bats-assert tillhandahåller tydliga påståendefunktioner som visar exakt vad som gick fel.
assert_success— kontrollerar att$statusär 0.assert_failure— kontrollerar att$statusinte är noll.assert_output— kontrollerar att$outputär lika med den angivna strängen.assert_output --partial— kontrollerar att utdatan innehåller delsträngen.refute_output --partial— kontrollerar att utdatan INTE innehåller delsträngen.
Installera genom att klona bats-core/bats-assert till en test/helpers/-mapp och läs sedan in det:
#!/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 och teardown: testlivscykelns krokar
Bats tillhandahåller två särskilda funktioner — setup och teardown — som körs automatiskt runt varje test. Använd dem för att förbereda och städa upp delat tillstånd, så att varje test startar i en känd miljö.
setup()körs före varje enskilt@test-block.teardown()körs efter varje enskilt@test-block, även om testet misslyckas.- Vanliga användningsområden är att skapa temporära kataloger, ange miljövariabler och ta bort temporära filer efter testet.
#!/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 och teardown_file: krokar på testsvitenivå
Ibland behöver Ni bara konfigurera resurskrävande resurser en gång per fil — inte före varje enskilt test. Bats tillhandahåller setup_file och teardown_file för detta.
setup_file()körs en gång före alla tester i filen.teardown_file()körs en gång efter alla tester i filen.- Använd
BATS_FILE_TMPDIR(som är tillgänglig automatiskt) för att dela data mellansetup_fileoch Era tester — vanliga variabler bevaras inte mellan subshells.
Ett typiskt användningsfall är att starta en mockserver eller bygga en binär en gång och sedan avsluta den i slutet:
#!/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"
}Testa funktioner som ändrar filer
Ett mycket vanligt mönster är att testa Bash-funktioner som läser från eller skriver till filsystemet. Den centrala tekniken är att använda temporära kataloger (via mktemp -d i setup), så att tester aldrig rör riktiga filer och aldrig stör varandra.
- Arbeta alltid i
$TEST_DIR(eller$BATS_TEST_TMPDIR— som är tillgänglig automatiskt i nyare versioner av Bats). - Använd hjälpbiblioteket
bats-fileför tydliga filpåståenden somassert_file_existsochassert_file_contains. - Hårdkoda aldrig sökvägar som
/tmp/myfile— parallella testkörningar kommer att krocka.
#!/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"
}Mocka externa kommandon
Funktioner anropar ofta externa program som curl, aws eller git. I enhetstester vill Ni testa Er logik, inte det riktiga externa kommandot. Den enklaste tekniken för mocking i Bats är att definiera en shellfunktion med samma namn som kommandot i setup — den får företräde framför den riktiga binärfilen.
- Definiera en funktion som
curl() { echo 'mocked response'; return 0; }isetupoch exportera den. - Använd
export -f curlså att funktionen är synlig i subshells som skapas avrun. - Ni kan också skriva mocken till en temporär fil på
PATHför mer komplexa scenarier.
#!/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"
}Hoppa över tester och tagga dem
Alla tester kan inte alltid köras — ibland krävs en riktig nätverksanslutning, ett visst verktyg eller ett specifikt operativsystem. Bats tillhandahåller skip för att villkorligt hoppa över ett test med ett informativt meddelande, i stället för att kommentera bort det eller få testsviten att gå sönder.
- Anropa
skip "reason"var som helst i ett@test-block för att hoppa över testet. - Överhoppade tester visas som
Si utdatan och räknas inte som misslyckanden. - Bats 1.5+ har stöd för taggar: märk tester med
# bats test_tags=slow,networkoch filtrera medbats --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/Strukturera en komplett testsvit
Ett välorganiserat Bats-projekt följer en förutsägbar katalogstruktur, vilket gör det enkelt att introducera nya medarbetare och integrera projektet med CI-pipelines.
Rekommenderad struktur:
lib/— Bash-funktioner för produktion (en fil per ansvarsområde:math.sh,fileutils.sh).test/— en.bats-fil per biblioteksfil (math.bats,fileutils.bats).test/helpers/— bats-assert, bats-file och bats-support som Git-submoduler.Makefile— etttest-mål så att medarbetare bara behöver köramake test.
Kör hela testsviten med ett enda kommando:
# 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
# `-- MakefileKunskapskontroll: Bats-core-påståenden
Testa Er förståelse av den centrala testmekanismen i Bats-core.
Sammanfattning: enhetstesta Bash med Bats-core
I den här lektionen lärde Ni Er att strukturera och skriva enhetstester för enskilda Bash-funktioner med Bats-core. Här är de viktigaste punkterna:
- Testfilens struktur — använd shebang-raden
#!/usr/bin/env batsoch@test-block med beskrivande namn. load— läs in Era biblioteksfiler så att funktionerna är tillgängliga i testerna utan att Ni behöver kopiera och klistra in dem.run+$status+$output— den centrala trion; använd alltidrunför att fånga resultat utan att testet omedelbart misslyckas.- bats-assert — föredra
assert_success,assert_failureochassert_outputframför råa[ ]för läsbara felmeddelanden. setup/teardown— körs före/efter varje test;setup_file/teardown_filekörs en gång per fil.- Mocking — ersätt externa kommandon genom att skugga dem med shellfunktioner med samma namn, exporterade med
export -f. skip— hoppa villkorligt över tester som är beroende av resurser som inte är tillgängliga.- Projektlayout — håll
lib/,test/ochtest/helpers/åtskilda för bättre underhåll och CI-integration.
Dessa mönster ger Era Bash-projekt samma disciplin i testningen som Ni skulle tillämpa på vilket modernt programvaruprojekt som helst.
Lär dig DevOps-bootcamp med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 142
- Lektioner
- 568
Vanliga frågor
Är lektionen ”Enhetstesta funktioner med Bats-core” gratis?
Ja – du kan läsa vilka 3 lektioner som helst i lärvägen DevOps-bootcamp, inklusive ”Enhetstesta funktioner med Bats-core”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i DevOps-bootcamp innehåller totalt 4 lektioner.
Vad lär jag mig i ”Enhetstesta funktioner med Bats-core”?
Strukturera testfiler, assertioner och setup/teardown för att verifiera enskilda Bash-funktioner. Ni övar på DevOps-bootcamp med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig DevOps-bootcamp?
Du behöver inga förkunskaper. Utbildningen i DevOps-bootcamp på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.
Hur lång tid tar lektionen ”Enhetstesta funktioner med Bats-core”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här DevOps-bootcamp-lektionen?
Ja. Varje DevOps-bootcamp-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Enhetstesta funktioner med Bats-core
- Mocka kommandon och stubba externa verktyg
- Testfixturer, temporära miljöer och täckningsgrad
- Kör skaltester i CI-pipelines