Bemästra Linux-kommandoraden och Bash-skriptning · Lektion

Mocka kommandon och stubba externa verktyg

Ändra PATH och definiera falska binärer för att testa skript utan att påverka riktiga system.

Lektion 2 av 413 steg

Mocka kommandon och stubba externa verktyg är en gratis lektion i Bemästra Linux-kommandoraden och Bash-skriptning på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Bemästra Linux-kommandoraden och Bash-skriptning, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Bemästra Linux-kommandoraden och Bash-skriptning innehåller totalt 4 lektioner.

Varför mocka kommandon i Bash-tester?

När Ni testar ett Bash-skript som anropar curl, aws, git eller något annat externt verktyg uppstår ett problem: riktiga anrop når nätverket, ändrar tillstånd, kostar pengar eller misslyckas helt enkelt i en CI-miljö där verktygen inte är installerade.

Mocking innebär att det riktiga kommandot ersätts med ett falskt som Ni kontrollerar. Er falska implementation (en stub) returnerar förutsägbara utdata och slutstatusar, så att testet blir snabbt, isolerat och reproducerbart.

  • Ingen åtkomst till nätverk eller molntjänster behövs
  • Tester körs på millisekunder i stället för sekunder
  • Ni kan simulera fel som är svåra att utlösa i riktiga system
  • CI-pipelines förblir rena och fria från beroenden

Bash ger Er en förvånansvärt enkel mekanism för detta: placera bara Er falska binärfil någonstans tidigare i $PATH än den riktiga.

Så fungerar PATH-sökning

När shellen kör ett kommando som curl söker den igenom varje katalog i $PATH från vänster till höger och kör den första träff den hittar.

Det innebär att shellen aldrig når /usr/bin/curl om Ni lägger till en katalog först som innehåller Ert eget curl-skript.

Mönstret för att åsidosätta kommandon:

  1. Skapa en temporär katalog (Er stub-bin)
  2. Skriv en falsk körbar fil med samma namn som det riktiga kommandot
  3. Lägg till katalogen först i PATH
  4. Kör skriptet som testas — det anropar Er stub i stället för den riktiga binärfilen
  5. Städa bort den temporära katalogen efter testet

Detta fungerar utan root-åtkomst, utan att systemfiler ändras och utan något särskilt ramverk.

Skapa en stubkatalog

mktemp -d för att skapa en isolerad temporär katalog för Era stubbar. Varje test eller testsvit får sin egen katalog, vilket förhindrar att tester påverkar varandra.

När testet är klart tar Ni bort katalogen med rm -rf. Med en trap säkerställs städningen även när testet avslutas i förtid på grund av ett fel.

#!/usr/bin/env bash
# Setup a stub bin directory for testing

# Create the temp dir
STUB_BIN=$(mktemp -d)

# Always clean up on exit (success, error, or signal)
trap 'rm -rf "$STUB_BIN"' EXIT

# Prepend it to PATH so our stubs take priority
export PATH="$STUB_BIN:$PATH"

echo "Stub bin: $STUB_BIN"
echo "PATH starts with: ${PATH%%:*}"

# Your tests would go here...
echo "Tests complete."

Skriv Er första stub

En stub är helt enkelt en körbar fil med samma namn som kommandot Ni vill ersätta. Den skriver ut den utdata som skriptet som testas förväntar sig och avslutas med den kod Ni väljer.

Viktiga regler för stubbar:

  • Filen måste vara körbar (chmod +x)
  • Shebang-raden (#!/usr/bin/env bash) krävs
  • Skriv ut den utdata som Ert riktiga skript skulle tolka
  • Använd exit 0 vid lyckat resultat och ett värde som inte är noll vid simulerade fel
#!/usr/bin/env bash
# Create a stub for 'curl' that returns a fake HTTP response

STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"

# Write the stub
cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
# Fake curl: always returns a 200 OK with a JSON body
echo '{"status": "ok", "version": "1.2.3"}'
exit 0
EOF
chmod +x "$STUB_BIN/curl"

# Verify the stub is found before the real curl
which curl
curl https://example.com/api/version

Testa ett skript som anropar curl

Nu sätter vi ihop delarna. Anta att Ni har ett distributionsskript som anropar curl för att kontrollera en hälsoendpunkt och sedan avslutas med ett fel om tjänsten inte är frisk. Ni vill testa både det lyckade fallet och felvägen utan en riktig server.

#!/usr/bin/env bash
# Script under test: check_health.sh
# It calls curl and checks the returned JSON

check_health() {
  local url="$1"
  local response
  response=$(curl -sf "$url")
  if [[ "$response" == *'"healthy":true'* ]]; then
    echo "Service is UP"
    return 0
  else
    echo "Service is DOWN" >&2
    return 1
  fi
}

# ---- Test harness ----
STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"

# Happy path stub
cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
echo '{"healthy":true}'
EOF
chmod +x "$STUB_BIN/curl"

check_health "http://fake-host/health" && echo "PASS: healthy response"

Simulera kommandofel

En av de mest värdefulla användningarna av stubbar är att simulera fel som är svåra att återskapa med riktiga verktyg – nätverkstimeouter, behörighetsfel, fulla diskar eller ett fjärr-API som returnerar 500.

För att simulera ett fel låter du helt enkelt stubben avslutas med en kod som inte är noll. Du kan också skriva till stderr precis som det riktiga kommandot skulle göra, så att skriptets felhantering testas fullt ut.

#!/usr/bin/env bash
# Test that check_health handles a curl failure gracefully

check_health() {
  local url="$1"
  local response
  # -f makes curl exit non-zero on HTTP error; -s silences progress
  if ! response=$(curl -sf "$url" 2>/dev/null); then
    echo "ERROR: could not reach $url" >&2
    return 1
  fi
  echo "OK: $response"
}

STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"

# Failure stub — simulates a network error (curl exit code 6 = could not resolve host)
cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
echo 'curl: (6) Could not resolve host: fake-host' >&2
exit 6
EOF
chmod +x "$STUB_BIN/curl"

if ! check_health "http://fake-host/health"; then
  echo "PASS: failure path handled correctly"
fi

Logga stubbanrop för verifiering

Ibland behöver du kontrollera inte bara vad skriptet skriver ut, utan också hur det anropar ett externt verktyg – vilka argument det skickar, hur många gånger det anropas eller i vilken ordning. En spionstub loggar sina anrop till en fil.

Efter testet läser testharnessen loggfilen och kontrollerar dess innehåll. Det ger dig verifiering på argumentnivå utan något särskilt ramverk.

#!/usr/bin/env bash
# Spy stub: record every invocation of 'aws' to a log file

STUB_BIN=$(mktemp -d)
CALL_LOG=$(mktemp)
trap 'rm -rf "$STUB_BIN" "$CALL_LOG"' EXIT
export PATH="$STUB_BIN:$PATH"
export CALL_LOG   # make it available inside the stub

cat > "$STUB_BIN/aws" << 'EOF'
#!/usr/bin/env bash
# Append all arguments to the call log
echo "aws $*" >> "$CALL_LOG"
# Return fake S3 output
echo "upload: ./report.pdf to s3://my-bucket/report.pdf"
exit 0
EOF
chmod +x "$STUB_BIN/aws"

# Simulate the script under test calling aws s3 cp
aws s3 cp report.pdf s3://my-bucket/report.pdf
aws s3 cp logs.tar.gz s3://my-bucket/logs.tar.gz

# Verify calls were made with expected arguments
echo "--- Recorded calls ---"
cat "$CALL_LOG"
grep -q 's3://my-bucket/report.pdf' "$CALL_LOG" && echo "PASS: S3 upload verified"

Stubbning av flera kommandon samtidigt

Ett verkligt skript anropar ofta flera externa verktyg. Du kan stubba alla i samma STUB_BIN-katalog. Varje stubfil är fristående och kan returnera olika utdata och avslutskoder.

Håll stubbarna minimala: returnera bara det som skriptet under test faktiskt tolkar. Försök inte simulera alla flaggor – använd bara den delmängd som skriptet använder.

#!/usr/bin/env bash
# Stub both 'git' and 'docker' for a release script test

STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"

# Stub git: pretend we are on tag v2.1.0
cat > "$STUB_BIN/git" << 'EOF'
#!/usr/bin/env bash
case "$*" in
  *"describe --tags"*) echo "v2.1.0" ;;
  *"rev-parse HEAD"*)  echo "abc1234" ;;
  *) echo "[git stub] unhandled: $*" >&2 ; exit 1 ;;
esac
EOF
chmod +x "$STUB_BIN/git"

# Stub docker: pretend build and push succeed
cat > "$STUB_BIN/docker" << 'EOF'
#!/usr/bin/env bash
echo "[docker stub] $*"
exit 0
EOF
chmod +x "$STUB_BIN/docker"

# Simulate the release logic
VERSION=$(git describe --tags)
SHA=$(git rev-parse HEAD)
echo "Building image for version=$VERSION sha=$SHA"
docker build -t "myapp:$VERSION" .
docker push "myapp:$VERSION"

Använda funktioner som stubbar (inga filer behövs)

I enkla fall behöver du inte skriva några filer alls. Du kan definiera en skalfunktion med samma namn som kommandot. Eftersom funktioner matchas före sökning i den externa PATH:en får de automatiskt företräde.

Detta är den snabbaste metoden för enhetstestning av skript som läses in med source. Funktionsstubbar fungerar dock bara i samma skalprocess – de är inte synliga för underskal som startas med uttryckligt bash -c eller för bakgrundsprocesser. Använd den filbaserade metoden i sådana fall.

#!/usr/bin/env bash
# Source the script under test (a small helper library)
source_under_test() {
  # Inline the logic we want to test
  get_instance_id() {
    # Would normally call: curl http://169.254.169.254/latest/meta-data/instance-id
    curl -sf http://169.254.169.254/latest/meta-data/instance-id
  }
}
source_under_test

# Override curl with a shell function stub
curl() {
  echo "i-0abc123def456"
  return 0
}
# Export is NOT needed — function is visible in same shell

# Run the function under test
result=$(get_instance_id)
[[ "$result" == "i-0abc123def456" ]] && echo "PASS: instance ID returned" || echo "FAIL"

Exportera funktioner till underskal

När skriptet under test startar ett underskal (t.ex. bash script.sh eller en pipeline) är skalfunktioner som definierats i det överordnade skalet intevs ärvs som standard. Du har två alternativ:

  • Använd export -f function_name för att exportera funktionen – då blir den tillgänglig i underordnade bash-processer
  • Eller använd filbaserade stubbar i en STUB_BIN-katalog, vilket alltid fungerar över processgränser

export -f är elegant, men fungerar bara med bash (inte med sh eller andra skal). Föredra filbaserade stubbar i polyglotta CI-miljöer.

#!/usr/bin/env bash
# Demonstrate export -f for subshell-visible function stubs

# Define the stub in the current shell
curl() {
  echo '{"status":"ok"}'
  return 0
}
# Export the function so child bash processes inherit it
export -f curl

# Verify the stub works in a subshell
bash -c '
  response=$(curl -sf http://api.example.com/status)
  echo "Subshell got: $response"
'

# Without export -f, the subshell would call the real curl
# (or fail if curl is not installed)

Integrera stubbar med ett testramverk (BATS)

När du använder BATS (Bash Automated Testing System) hör stubbförberedelserna hemma i hooken setup(), och nedstängningen i teardown(). BATS återställer miljön mellan testerna, så varje test får en ny stubkatalog.

BATS-variabler som $BATS_TEST_TMPDIR ger dig automatiskt en temporär katalog per test – använd den i stället för mktemp -d för renare kod.

#!/usr/bin/env bats
# File: test_deploy.bats
# Run with: bats test_deploy.bats

setup() {
  # BATS provides a unique tmpdir per test
  export STUB_BIN="$BATS_TEST_TMPDIR/stub_bin"
  mkdir -p "$STUB_BIN"
  export PATH="$STUB_BIN:$PATH"

  # Default stub: healthy service
  cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
echo '{"healthy":true}'
EOF
  chmod +x "$STUB_BIN/curl"
}

teardown() {
  # BATS auto-removes BATS_TEST_TMPDIR, but explicit is safer
  rm -rf "$STUB_BIN"
}

@test "deploy succeeds when service is healthy" {
  run bash deploy.sh
  [ "$status" -eq 0 ]
  [[ "$output" == *"Deploy complete"* ]]
}

@test "deploy aborts when service is down" {
  # Override the stub for this specific test
  echo -e '#!/usr/bin/env bash\nexit 1' > "$STUB_BIN/curl"
  chmod +x "$STUB_BIN/curl"

  run bash deploy.sh
  [ "$status" -ne 0 ]
}

Kunskapskontroll: Mockning av kommandon i Bash

Testa dina kunskaper om kommandomockning och stubbmönster i Bash.

En utvecklare skriver ett test som definierar en skalfunktion med namnet aws för att stubba det riktiga AWS CLI. Testet fungerar bra när det körs direkt i terminalen, men när CI-pipelinen kör skriptet under test som bash deploy.sh ignoreras stubben och det riktiga kommandot aws anropas.

Vad är den korrekta lösningen?

Sammanfattning: Mocka kommandon och stubba externa verktyg

Du har lärt dig hela verktygslådan för att ersätta riktiga kommandon med kontrollerade ersättningar när du testar Bash-skript.

Viktiga tekniker:

  • PATH-företräde – skapa en STUB_BIN-katalog med mktemp -d, skriv körbara stubfiler där och placera katalogen först i PATH
  • Stubbers avslutskoder – returnera 0 vid lyckat resultat och värden som inte är noll för att simulera specifika fel (nätverksfel, nekad åtkomst osv.)
  • Spionstubbar – lägg till argument i en loggfil inifrån stubben för att kontrollera hur skriptet anropade externa verktyg
  • Flera stubbar – placera flera stubfiler i samma STUB_BIN för att mocka ett helt ekosystem av beroenden samtidigt
  • Funktionsstubbar – definiera en skalfunktion med samma namn som ett kommando för mockning i samma process; använd export -f för att nå underordnade bash-processer
  • BATS-integrering – använd hookarna setup()/teardown() och $BATS_TEST_TMPDIR för ren isolering per test

Använd alltid en trap '...' EXIT för att garantera att stubbarna städas bort oavsett testresultat. Håll stubbarna minimala – returnera bara det som skriptet faktiskt tolkar. Filbaserade stubbar är det mest portabla valet för CI-pipelines.

Gratis att börja

Lär dig Bash 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
22
Lektioner
88

Vanliga frågor

Är lektionen ”Mocka kommandon och stubba externa verktyg” gratis?

Ja – hela texten till ”Mocka kommandon och stubba externa verktyg” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Bemästra Linux-kommandoraden och Bash-skriptning, kan Ni uppgradera till CoddyKit PRO. Kursen i Bemästra Linux-kommandoraden och Bash-skriptning innehåller totalt 4 lektioner.

Vad lär jag mig i ”Mocka kommandon och stubba externa verktyg”?

Ändra PATH och definiera falska binärer för att testa skript utan att påverka riktiga system. Ni övar på Bemästra Linux-kommandoraden och Bash-skriptning 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 Bemästra Linux-kommandoraden och Bash-skriptning?

Du behöver inga förkunskaper. Utbildningen i Bemästra Linux-kommandoraden och Bash-skriptning 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 2 av 4.

Hur lång tid tar lektionen ”Mocka kommandon och stubba externa verktyg”?

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 Bemästra Linux-kommandoraden och Bash-skriptning-lektionen?

Ja. Varje Bemästra Linux-kommandoraden och Bash-skriptning-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 Bemästra Linux-kommandoraden och Bash-skriptning