Linux-kommandolinjen og Bash-scripting på ekspertniveau · Lektion

Enhedstest af funktioner med Bats-core

Strukturér testfiler, assertions og setup/teardown for at verificere individuelle Bash-funktioner.

Lektion 1 af 413 trin

Enhedstest af funktioner med Bats-core er en gratis Linux-kommandolinjen og Bash-scripting på ekspertniveau-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Linux-kommandolinjen og Bash-scripting på ekspertniveau, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Linux-kommandolinjen og Bash-scripting på ekspertniveau-kurset indeholder 4 lektioner i alt.

Hvad er Bats-core, og hvorfor bruge det?

Bats-core (Bash Automated Testing System) er det uofficielle standardrammeværk til enhedstest af Bash. Det lader dig skrive strukturerede, gentagelige test af dine shell-funktioner og -scripts — på samme måde som du ville bruge JUnit til Java eller pytest til Python.

  • Hver test er en @test-blok med en beskrivelse, der er let at forstå.
  • Test består, når hver kommando i den returnerer afslutningskode 0.
  • Test mislykkes ved den første afslutningskode, der ikke er nul, eller ved en mislykket påstand.
  • Outputtet er TAP-kompatibelt, så CI-systemer (GitHub Actions, Jenkins, GitLab CI) forstår det direkte.

Installer via din pakkehåndtering, eller klon lageret:

# 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

Din første Bats-testfil

En Bats-testfil har filtypenavnet .bats og begynder med en særlig shebang. Den centrale byggesten er direktivet @test efterfulgt af en beskrivende streng og en blok med kommandoer.

  • Shebang-linjen #!/usr/bin/env bats fortæller shellen, hvordan filen skal køres.
  • Hver @test-blok er en uafhængig testsag.
  • Du kan køre en enkelt fil med bats my_tests.bats eller en hel mappe med bats test/.

Nedenfor ses den minimale struktur for 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
}

Indlæsning af funktionen, der skal testes, med 'load'

I rigtige projekter ligger dine Bash-funktioner i biblioteksfiler, ikke selve testfilen. Bats har hjælpefunktionen load, som indlæser eksterne filer relativt til testfilens mappe.

  • load '../lib/math.sh' indlæser filen, før hver test køres.
  • Efter indlæsningen er alle funktioner, der er defineret i filen, tilgængelige i dine testblokke.
  • Opbevar dine biblioteksfunktioner i en lib/-mappe og test i en test/-mappe for at holde dem adskilt.

Eksempel på projektlayout og den tilhørende 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" ]
}

Centrale påstande: run, $status, $output

Kommandoen run er kernen i Bats-test. I stedet for at køre en kommando direkte pakker du den ind i run, som opsamler dens afslutningskode og output uden at få testen til straks at mislykkes.

  • $status — indeholder afslutningskoden fra den seneste run-kommando.
  • $output — indeholder det samlede stdout fra den seneste run-kommando.
  • $lines — et array, hvor hvert element er én outputlinje (${lines[0]}, ${lines[1]} osv.).

Det lader dig kontrollere både tilfælde, hvor det lykkes, og tilfælde, hvor det mislykkes:

#!/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"* ]]
}

Brug af bats-assert til udtryksfulde påstande

De indbyggede [ ]-påstande fungerer, men giver dårlige fejlmeddelelser. Hjælpebiblioteket bats-assert indeholder udtryksfulde påstandsfunktioner, der viser præcis, hvad der gik galt.

  • assert_success — kontrollerer, at $status er 0.
  • assert_failure — kontrollerer, at $status ikke er nul.
  • assert_output — kontrollerer, at $output er lig med den angivne streng.
  • assert_output --partial — kontrollerer, at outputtet indeholder delstrengen.
  • refute_output --partial — kontrollerer, at outputtet IKKE indeholder delstrengen.

Installer ved at klone bats-core/bats-assert til en test/helpers/-mappe, og indlæs det derefter:

#!/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 og teardown: Testens livscykluskroge

Bats har to særlige funktioner — setup og teardown — som automatisk køres omkring hver test. Brug dem til at forberede og rydde op i delt tilstand, så hver test starter i et kendt miljø.

  • setup() køres før hver enkelt @test-blok.
  • teardown() køres efter hver enkelt @test-blok, også hvis testen mislykkes.
  • Almindelige anvendelser er at oprette midlertidige mapper, angive miljøvariabler og fjerne midlertidige filer efter testen.
#!/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 og teardown_file: Kroge på testsuiteniveau

Nogle gange har du kun brug for at klargøre ressourcekrævende ressourcer én gang pr. fil — ikke før hver eneste test. Bats har setup_file og teardown_file til dette formål.

  • setup_file() køres én gang før alle test i filen.
  • teardown_file() køres én gang efter alle test i filen.
  • Brug BATS_FILE_TMPDIR (tilgængelig automatisk) til at dele data mellem setup_file og dine test — almindelige variabler bevares ikke på tværs af subshells.

Et typisk anvendelsestilfælde er at starte en simuleringsserver eller bygge en binærfil én gang og derefter lukke den ned til sidst:

#!/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"
}

Test af funktioner, der ændrer filer

Et meget almindeligt mønster er at teste Bash-funktioner, der læser fra eller skriver til filsystemet. Den vigtigste teknik er at bruge midlertidige mapper (via mktemp -d i setup), så test aldrig rører rigtige filer og aldrig påvirker hinanden.

  • Arbejd altid i $TEST_DIR (eller $BATS_TEST_TMPDIR — tilgængelig automatisk i nyere Bats).
  • Brug hjælpebiblioteket bats-file til tydelige filpåstande som assert_file_exists og assert_file_contains.
  • Indkod aldrig stier som /tmp/myfile — parallelle testkørsler vil kollidere.
#!/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"
}

Simulering af eksterne kommandoer

Funktioner kalder ofte eksterne programmer som curl, aws eller git. I enhedstest vil du teste din logik, ikke den rigtige eksterne kommando. Den reneste Bats-teknik til simulering er at definere en shell-funktion med samme navn som kommandoen i setup — den har forrang for den rigtige binærfil.

  • Definer en funktion som curl() { echo 'mocked response'; return 0; } i setup, og eksportér den.
  • Brug export -f curl, så funktionen er synlig i subshells, der oprettes af run.
  • Du kan også skrive simuleringen til en midlertidig fil på PATH i mere komplekse 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"
}

Spring test over og tilføj mærker

Ikke alle test kan altid køres — nogle gange har du brug for en rigtig netværksforbindelse, et bestemt værktøj eller et bestemt operativsystem. Bats har skip, så du betinget kan springe en test over med en informativ meddelelse i stedet for at udkommentere den eller ødelægge testsuiten.

  • Kald skip "reason" hvor som helst i en @test-blok for at springe testen over.
  • Test, der springes over, vises som S i outputtet og tæller ikke som fejl.
  • Bats 1.5+ understøtter mærker: annotér test med # bats test_tags=slow,network, og filtrér med 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/

Strukturering af en komplet testsuite

Et velorganiseret Bats-projekt følger et forudsigeligt mappelayout, hvilket gør det nemt at få nye bidragydere i gang og integrere med CI-arbejdsgange.

Anbefalet struktur:

  • lib/ — Bash-funktioner til produktion (én fil pr. ansvarsområde: math.sh, fileutils.sh).
  • test/ — én .bats-fil pr. biblioteksfil (math.bats, fileutils.bats).
  • test/helpers/ — bats-assert, bats-file, bats-support som git-undermoduler.
  • Makefile — et test-mål, så bidragydere blot kan køre make test.

Kør hele testsuiten med én 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
# `-- Makefile

Videnstjek: Bats-core-påstande

Test din forståelse af Bats-cores centrale testmekanisme.

Opsummering: Enhedstest af Bash med Bats-core

I denne lektion lærte du at strukturere og skrive enhedstest for individuelle Bash-funktioner ved hjælp af Bats-core. Her er de vigtigste pointer:

  • Testfilens struktur — brug shebang-linjen #!/usr/bin/env bats og @test-blokke med beskrivende navne.
  • load — indlæs dine biblioteksfiler, så funktionerne er tilgængelige i test uden kopiering og indsættelse.
  • run + $status + $output — den centrale trio; brug altid run til at opsamle resultater uden at få testen til straks at mislykkes.
  • bats-assert — foretræk assert_success, assert_failure og assert_output frem for rå [ ] for læselige fejlmeddelelser.
  • setup / teardown — køres før/efter hver test; setup_file / teardown_file køres én gang pr. fil.
  • Simulering — tilsidesæt eksterne kommandoer med shell-funktioner med samme navn, eksporteret med export -f.
  • skip — spring betinget test over, der afhænger af ressourcer, som ikke er tilgængelige.
  • Projektlayout — hold lib/, test/ og test/helpers/ adskilt for at lette vedligeholdelse og CI-integration.

Disse mønstre giver dine Bash-projekter den samme disciplin inden for test, som du ville anvende på ethvert moderne softwareprojekt.

Gratis at komme i gang

Lær Bash med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
22
Lektioner
88

Ofte stillede spørgsmål

Er lektionen “Enhedstest af funktioner med Bats-core” gratis?

Ja — hele teksten til “Enhedstest af funktioner med Bats-core” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Linux-kommandolinjen og Bash-scripting på ekspertniveau-kurset, skal du opgradere til CoddyKit PRO. Linux-kommandolinjen og Bash-scripting på ekspertniveau-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Enhedstest af funktioner med Bats-core”?

Strukturér testfiler, assertions og setup/teardown for at verificere individuelle Bash-funktioner. Du øver dig i Linux-kommandolinjen og Bash-scripting på ekspertniveau med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Linux-kommandolinjen og Bash-scripting på ekspertniveau?

Der kræves ingen tidligere erfaring. Linux-kommandolinjen og Bash-scripting på ekspertniveau på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.

Hvor lang tid tager lektionen “Enhedstest af funktioner med Bats-core”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Linux-kommandolinjen og Bash-scripting på ekspertniveau-lektion?

Ja. Alle Linux-kommandolinjen og Bash-scripting på ekspertniveau-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Enhedstest af funktioner med Bats-core
  2. Mocking af kommandoer og stubbing af eksterne værktøjer
  3. Test-fixtures, midlertidige miljøer og dækning
  4. Kørsel af shelltests i CI-pipelines
← Tilbage til Linux-kommandolinjen og Bash-scripting på ekspertniveau