Mocking av kommandoer og stubbing av eksterne verktøy
Overstyr PATH og definer falske binærfiler for å teste skript uten å berøre virkelige systemer.
Mocking av kommandoer og stubbing av eksterne verktøy er en gratis leksjon i DevOps-bootcamp på CoddyKit. Dette er leksjon 2 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i DevOps-bootcamp, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i DevOps-bootcamp inneholder totalt 4 leksjoner.
Hvorfor mocke kommandoer i Bash-tester
Når De tester et Bash-skript som kaller curl, aws, git eller et annet eksternt verktøy, møter De et problem: Virkelige kall kobler seg til nettverket, endrer tilstand, koster penger eller mislykkes ganske enkelt i et CI-miljø der verktøyene ikke er installert.
Mocking betyr å erstatte den virkelige kommandoen med en falsk kommando som De kontrollerer. Den falske kommandoen (en stub) returnerer forutsigbare resultater og avslutningskoder, slik at testen blir rask, isolert og reproduserbar.
- Ingen tilgang til nettverk eller skytjenester er nødvendig
- Testene kjører på millisekunder i stedet for sekunder
- De kan simulere feil som er vanskelige å utløse på virkelige systemer
- CI-pipelines forblir ryddige og uten avhengigheter
Bash gir Dem en overraskende enkel mekanisme for dette: Plasser ganske enkelt den falske binærfilen et sted tidligere i $PATH enn den virkelige.
Slik fungerer oppslag i PATH
Når skallet kjører en kommando som curl, søker det gjennom hver katalog i $PATH fra venstre mot høyre og kjører det første treffet det finner.
Det betyr at hvis De legger en katalog som inneholder Deres egen curl-fil, først i PATH, vil skallet aldri nå frem til /usr/bin/curl.
Mønsteret for overstyring:
- Opprett en midlertidig katalog (Deres stub-binærkatalog)
- Skriv en falsk kjørbar fil med samme navn som den virkelige kommandoen
- Legg katalogen først i
PATH - Kjør skriptet som testes – det kaller stubben Deres, ikke den virkelige binærfilen
- Rydd opp i den midlertidige katalogen etter testen
Dette fungerer uten root-tilgang, uten at systemfiler endres og uten noe spesielt rammeverk.
Opprette en stub-katalog
Standardmønsteret bruker mktemp -d til å opprette en isolert midlertidig katalog for stubbene Deres. Hver test eller testpakke får sin egen katalog, slik at forurensning mellom tester unngås.
Fjern katalogen med rm -rf når testen er fullført. Ved å bruke en trap sikrer De opprydding selv når testen avsluttes tidlig på grunn av en feil.
#!/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."Skrive Deres første stub
En stub er ganske enkelt en kjørbar fil med samme navn som kommandoen De vil erstatte. Den skriver ut det resultatet skriptet som testes, forventer, og avslutter med koden De velger.
Viktige regler for stubber:
- Filen må være kjørbar (
chmod +x) - Shebang-linjen (
#!/usr/bin/env bash) er obligatorisk - Skriv ut resultatet som det virkelige skriptet ville tolket
- Bruk
exit 0ved suksess og en verdi som ikke er null ved simulerte feil
#!/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/versionTeste et skript som kaller curl
La oss nå sette det hele sammen. Anta at De har et distribusjonsskript som kaller curl for å kontrollere et endepunkt for helsestatus, og deretter avslutter med en feil hvis tjenesten ikke er frisk. De vil teste både normalforløpet og feilforløpet uten en ekte 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"Simulering av kommandofeil
En av de mest verdifulle bruksområdene for stubber er å simulere feil som er vanskelige å gjenskape med ekte verktøy – tidsavbrudd i nettverket, tillatelsesfeil, full disk eller et eksternt API som returnerer 500.
For å simulere en feil lar De ganske enkelt stubben avslutte med en kode som ikke er null. De kan også skrive til stderr nøyaktig slik den ekte kommandoen ville gjort, slik at feilhåndteringen i skriptet blir testet 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"
fiRegistrering av stubbkall for verifisering
Noen ganger må De kontrollere ikke bare hva skriptet skriver ut, men også hvordan det kalte et eksternt verktøy – hvilke argumenter det sendte, hvor mange ganger det ble kalt, eller i hvilken rekkefølge. En spionstubbe registrerer kallene sine i en fil.
Etter testen leser testkjøringen loggfilen og kontrollerer innholdet. Dette gir Dem verifisering på argumentnivå uten et spesielt rammeverk.
#!/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"Stubbing av flere kommandoer samtidig
Et ekte skript kaller ofte flere eksterne verktøy. De kan stubbe alle i den samme STUB_BIN-katalogen. Hver stubfil er uavhengig og kan returnere ulike utdata og avslutningskoder.
Hold stubbene minimale: returner bare det skriptet som testes, faktisk tolker. Ikke prøv å simulere hvert eneste flagg – bruk bare delmengden skriptet benytter.
#!/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"Bruke funksjoner som stubber (ingen filer nødvendig)
I enkle tilfeller trenger De ikke å skrive filer i det hele tatt. De kan definere en shell-funksjon med samme navn som kommandoen. Ettersom funksjoner slås opp før eksterne kommandoer via PATH, får de automatisk prioritet.
Dette er den raskeste tilnærmingen for enhetstesting av skript som er lastet inn med source. Funksjonsstubber fungerer imidlertid bare i den samme shell-prosessen – de blir ikke synlige for underskall som startes med eksplisitt bash -c, eller for prosesser som kjøres i bakgrunnen. Bruk den filbaserte tilnærmingen i slike tilfeller.
#!/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"Eksportere funksjoner til underskall
Når skriptet som testes, starter et underskall (for eksempel bash script.sh eller en rørkjede), blir shell-funksjonsstubber som er definert i det overordnede skallet, som standard ikke arvet. De har to alternativer:
- Bruk
export -f function_namefor å eksportere funksjonen – da blir den tilgjengelig i underordnedebash-prosesser - Eller gå tilbake til filbaserte stubber i en
STUB_BIN-katalog, som alltid fungerer på tvers av prosessgrenser
export -f er elegant, men fungerer bare med bash (ikke sh eller andre shell). Foretrekk filbaserte stubber i polyglotte 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)Integrere stubber med et testrammeverk (BATS)
Når De bruker BATS (Bash Automated Testing System), hører oppsett av stubber hjemme i setup()-kroken, og opprydding hører hjemme i teardown(). BATS tilbakestiller miljøet mellom testene, slik at hver test får en ny stubkatalog.
BATS-variabler som $BATS_TEST_TMPDIR gir Dem automatisk en midlertidig katalog per test – bruk den i stedet for mktemp -d for ryddigere kode.
#!/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 ]
}Kunnskapstest: Mocking av kommandoer i Bash
Test forståelsen Deres av mocking av kommandoer og stubmønstre i Bash.
En utvikler skriver en test som definerer en shell-funksjon med navnet aws for å stubbe den ekte AWS CLI-en. Testen fungerer fint når den kjøres direkte i terminalen, men når CI-pipelinen kjører skriptet som testes, med bash deploy.sh, ignoreres stubben, og den ekte aws-kommandoen blir kalt.
Hva er den riktige løsningen?
Oppsummering: Mocking av kommandoer og stubbing av eksterne verktøy
De har lært det komplette verktøysettet for å erstatte ekte kommandoer med kontrollerte forfalskninger under testing av Bash-skript.
Sentrale teknikker:
- PATH-foranlegging – opprett en
STUB_BIN-katalog medmktemp -d, skriv kjørbare stubfiler der, og legg katalogen først iPATH - Stub-avslutningskoder – returner
0ved suksess og verdier som ikke er null for å simulere bestemte feil (nettverksfeil, tilgang nektet og så videre) - Spionstubber – legg argumentene til i en loggfil i stubben for å kontrollere hvordan skriptet kalte eksterne verktøy
- Flere stubber – plasser flere stubfiler i den samme
STUB_BIN-katalogen for å mocke et helt økosystem av avhengigheter samtidig - Funksjonsstubber – definer en shell-funksjon med samme navn som en kommando for mocking i samme prosess; bruk
export -ffor å nå underordnede bash-prosesser - BATS-integrasjon – bruk
setup()/teardown()-kroker og$BATS_TEST_TMPDIRfor ren isolasjon per test
Bruk alltid en trap '...' EXIT for å garantere opprydding av stubber uansett testresultat. Hold stubbene minimale – returner bare det skriptet faktisk tolker. Filbaserte stubber er det mest portable valget for CI-pipelines.
Lær deg DevOps-bootcamp med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 142
- Leksjoner
- 568
Ofte stilte spørsmål
Er leksjonen «Mocking av kommandoer og stubbing av eksterne verktøy» gratis?
Ja – du kan lese valgfritt 3 av leksjonene i læringsstien DevOps-bootcamp, inkludert «Mocking av kommandoer og stubbing av eksterne verktøy», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i DevOps-bootcamp inneholder totalt 4 leksjoner.
Hva lærer jeg i «Mocking av kommandoer og stubbing av eksterne verktøy»?
Overstyr PATH og definer falske binærfiler for å teste skript uten å berøre virkelige systemer. Du øver på DevOps-bootcamp med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med DevOps-bootcamp?
Ingen tidligere erfaring er nødvendig. DevOps-bootcamp på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.
Hvor lang tid tar leksjonen «Mocking av kommandoer og stubbing av eksterne verktøy»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne DevOps-bootcamp-leksjonen?
Ja. Alle DevOps-bootcamp-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Enhetstesting av funksjoner med Bats-core
- Mocking av kommandoer og stubbing av eksterne verktøy
- Testfiksturer, midlertidige miljøer og dekningsgrad
- Kjøring av skalltester i CI-pipelines