DevOps-bootcamp · Lektion

Enhetstesta funktioner med Bats-core

Strukturera testfiler, assertioner och setup/teardown för att verifiera enskilda Bash-funktioner.

Lektion 1 av 413 steg

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.y

Ert 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 bats talar 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.bats eller en hel katalog med bats 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 en test/-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 senaste run-kommandot.
  • $output — innehåller den sammanlagda standardutmatningen från det senaste run-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 $status inte ä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 mellan setup_file och 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-file för tydliga filpåståenden som assert_file_exists och assert_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; } i setup och exportera den.
  • Använd export -f curl så att funktionen är synlig i subshells som skapas av run.
  • Ni kan också skriva mocken till en temporär fil på PATH fö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 S i utdatan och räknas inte som misslyckanden.
  • Bats 1.5+ har stöd för taggar: märk tester med # bats test_tags=slow,network och filtrera 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/

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 — ett test-mål så att medarbetare bara behöver köra make 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
# `-- Makefile

Kunskapskontroll: 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 bats och @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 alltid run för att fånga resultat utan att testet omedelbart misslyckas.
  • bats-assert — föredra assert_success, assert_failure och assert_output framför råa [ ] för läsbara felmeddelanden.
  • setup / teardown — körs före/efter varje test; setup_file / teardown_file kö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/ och test/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.

Gratis att börja

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

  1. Enhetstesta funktioner med Bats-core
  2. Mocka kommandon och stubba externa verktyg
  3. Testfixturer, temporära miljöer och täckningsgrad
  4. Kör skaltester i CI-pipelines
← Tillbaka till DevOps-bootcamp