0Pricing
Linux Command Line & Bash Scripting Mastery · Pelajaran

Fixture, Environment Sementara, dan Cakupan

Bangun fixture pengujian yang terisolasi dan ukur cabang skrip mana yang benar-benar dijalankan oleh pengujian Anda.

Fixture, Environment Sementara, dan Cakupan adalah pelajaran Linux Command Line & Bash Scripting Mastery gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Linux Command Line & Bash Scripting Mastery, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Linux Command Line & Bash Scripting Mastery mencakup 4 pelajaran total.

Mengapa Fixture dan Isolasi Itu Penting

Saat menguji skrip Bash, risiko terbesar adalah efek samping: pengujian Anda tanpa sengaja mengubah file nyata, basis data nyata, atau keadaan sistem nyata. Pengujian yang berhasil di komputer Anda tetapi merusak data produksi lebih buruk daripada tidak memiliki pengujian sama sekali.

Solusinya adalah fixture pengujian—lingkungan terkendali dan sementara yang mencerminkan kondisi nyata tanpa menyentuh apa pun yang sebenarnya. Fixture yang baik memberi Anda:

  • Dapat direproduksi—pengujian menghasilkan hasil yang sama setiap kali dijalankan
  • Isolasi—pengujian tidak saling mengganggu atau mengganggu host
  • Keamanan—operasi destruktif hanya menyentuh data yang boleh dibuang
  • Kecepatan—tanpa pemanggilan jaringan atau I/O berat kecuali benar-benar diperlukan

Dalam pengujian Bash, fixture biasanya berupa direktori sementara yang diisi file-file yang telah ditentukan, berkas eksekusi tiruan yang ditempatkan di awal $PATH, serta variabel lingkungan yang cakupannya dibatasi pada proses pengujian.

Membuat dan Membersihkan Direktori Sementara

Pola standar untuk direktori sementara per pengujian menggunakan mktemp -d, yang membuat direktori unik di /tmp dan mencetak jalurnya. Anda menyimpan jalur tersebut dan mendaftarkan trap untuk menghapusnya secara otomatis saat shell keluar—bahkan ketika terjadi kegagalan.

Idiom dua baris ini seharusnya ada di setiap file pengujian yang menyentuh sistem berkas:

#!/usr/bin/env bash
set -euo pipefail

# Create an isolated temp directory
TMPDIR=$(mktemp -d)
# Always clean up, even if the script exits early or errors out
trap 'rm -rf "$TMPDIR"' EXIT

echo "Working in: $TMPDIR"

# Simulate creating fixture files
mkdir -p "$TMPDIR/project/{src,tests,logs}"
echo 'version=1.2.3' > "$TMPDIR/project/.env"
echo 'Hello fixture' > "$TMPDIR/project/src/main.sh"

ls -R "$TMPDIR/project"
echo 'Temp dir will be removed automatically on exit'

Menyusun Struktur Pohon Direktori Fixture

Fixture yang tersusun baik mencerminkan tata letak direktori yang benar-benar diharapkan oleh skrip yang diuji. Anggaplah fixture sebagai akar proyek tiruan berukuran kecil. Fungsi penyiapan fixture membuat tata letak ini sebelum setiap pengujian, dan pembersihan menghapusnya.

Praktik utama:

  • Gunakan fungsi setup() yang dipanggil oleh kerangka kerja pengujian sebelum setiap pengujian
  • Gunakan teardown() atau cleanup() yang berjalan setelah setiap pengujian, bahkan saat terjadi kegagalan
  • Jaga agar file fixture tetap minimal—hanya yang benar-benar dibaca oleh skrip
  • Beri nama file fixture secara deskriptif agar kegagalan mudah didiagnosis
#!/usr/bin/env bash
# fixture_helpers.bash — source this from your test files

FIXTURE_ROOT=''

setup_fixture() {
  FIXTURE_ROOT=$(mktemp -d)
  # Build the directory tree the deploy script expects
  mkdir -p "$FIXTURE_ROOT"/{dist,config,logs}
  echo '{"version":"2.0"}' > "$FIXTURE_ROOT/config/app.json"
  echo 'console.log("app")' > "$FIXTURE_ROOT/dist/index.js"
  touch "$FIXTURE_ROOT/logs/.gitkeep"
  export FIXTURE_ROOT
  echo "[setup] Fixture ready at $FIXTURE_ROOT"
}

teardown_fixture() {
  if [[ -n "$FIXTURE_ROOT" && -d "$FIXTURE_ROOT" ]]; then
    rm -rf "$FIXTURE_ROOT"
    echo '[teardown] Fixture removed'
  fi
}

# Self-test
setup_fixture
ls "$FIXTURE_ROOT"
teardown_fixture

Meniru Berkas Eksekusi dengan PATH Tiruan

Banyak skrip Bash memanggil alat eksternal seperti curl, aws, docker, atau git. Dalam pengujian, Anda tidak ingin mengakses layanan nyata, jadi Anda mengganti alat tersebut dengan berkas eksekusi tiruan.

Tekniknya sederhana:

  1. Buat direktori sementara bin/ di dalam fixture
  2. Tulis skrip shell kecil di sana dengan nama yang sama seperti alat nyata
  3. Tambahkan direktori tersebut di awal $PATH sebelum memanggil skrip yang diuji

Karena $PATH dicari dari kiri ke kanan, berkas tiruan akan dipilih. Berkas biner nyata tidak pernah dipanggil.

#!/usr/bin/env bash
set -euo pipefail

FIXTURE=$(mktemp -d)
trap 'rm -rf "$FIXTURE"' EXIT

# Create a fake 'curl' that records calls and returns canned data
FAKE_BIN="$FIXTURE/bin"
mkdir -p "$FAKE_BIN"

cat > "$FAKE_BIN/curl" << 'SCRIPT'
#!/usr/bin/env bash
# Log every argument for later inspection
echo "curl $*" >> "$FIXTURE_BIN_LOG"
# Return a canned HTTP 200 response body
echo '{"status":"ok","id":42}'
SCRIPT
chmod +x "$FAKE_BIN/curl"

# Export the log path so the fake can find it
export FIXTURE_BIN_LOG="$FIXTURE/curl_calls.log"

# Prepend fake bin directory to PATH
export PATH="$FAKE_BIN:$PATH"

# Now any call to 'curl' hits our fake
curl -s https://api.example.com/health
curl -X POST https://api.example.com/deploy

echo '--- Recorded curl calls ---'
cat "$FIXTURE_BIN_LOG"

Menangkap dan Memastikan Keluaran Perintah

Fixture hanya berguna jika Anda dapat memastikan tindakan skrip Anda. Pola standarnya adalah:

  • Tangkap stdout/stderr ke dalam variabel dengan $() atau substitusi proses
  • Periksa file log yang ditulis oleh berkas biner tiruan
  • Periksa kode keluar secara eksplisit dengan $? atau logika kondisional
  • Pastikan file tertentu dibuat, diubah, atau dibiarkan tanpa perubahan

Menulis pembantu pemeriksaan yang kecil dan terfokus membuat pengujian Anda mudah dibaca serta memberikan pesan kegagalan yang tepat saat terjadi kesalahan.

#!/usr/bin/env bash
set -euo pipefail

# Minimal assertion helpers
assert_eq() {
  local desc="$1" expected="$2" actual="$3"
  if [[ "$expected" == "$actual" ]]; then
    echo "PASS: $desc"
  else
    echo "FAIL: $desc"
    echo "  expected: $expected"
    echo "  actual:   $actual"
    return 1
  fi
}

assert_file_exists() {
  local desc="$1" file="$2"
  if [[ -f "$file" ]]; then
    echo "PASS: $desc"
  else
    echo "FAIL: $desc — file not found: $file"
    return 1
  fi
}

assert_contains() {
  local desc="$1" needle="$2" haystack="$3"
  if [[ "$haystack" == *"$needle"* ]]; then
    echo "PASS: $desc"
  else
    echo "FAIL: $desc — '$needle' not found in output"
    return 1
  fi
}

# Demo usage
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
echo 'hello world' > "$TMPDIR/greeting.txt"
OUT=$(cat "$TMPDIR/greeting.txt")
assert_eq   'file content matches'     'hello world' "$OUT"
assert_file_exists 'greeting file created' "$TMPDIR/greeting.txt"
assert_contains    'output has hello'     'hello'       "$OUT"

Membatasi Cakupan Variabel Lingkungan dalam Pengujian

Skrip sering membaca variabel lingkungan seperti $HOME, $CONFIG_PATH, atau $DATABASE_URL. Dalam pengujian, Anda harus mengganti nilainya tanpa mencemari lingkungan nyata.

Pendekatan yang paling aman adalah menjalankan skrip yang diuji dalam subshell yang hanya berisi variabel yang Anda tetapkan secara eksplisit. Perintah env memungkinkan Anda menghapus seluruh lingkungan lalu menambahkan kembali hanya yang diperlukan:

  • env -i VAR=val ./script.sh—lingkungan yang sepenuhnya bersih
  • (export VAR=val; ./script.sh)—subshell mewarisi lingkungan proses induk dan menambahkan penggantian Anda

Menggunakan subshell juga berarti jika skrip mengubah $IFS, $PWD, atau keadaan global lainnya, perubahan tersebut tidak pernah keluar kembali ke pelaksana pengujian.

#!/usr/bin/env bash
set -euo pipefail

FIXTURE=$(mktemp -d)
trap 'rm -rf "$FIXTURE"' EXIT

# Write a tiny script under test that reads env vars
cat > "$FIXTURE/deploy.sh" << 'SCRIPT'
#!/usr/bin/env bash
set -euo pipefail
ENV_NAME=${DEPLOY_ENV:-unknown}
CFG=${CONFIG_DIR:-/etc/app}
echo "Deploying to $ENV_NAME using config from $CFG"
SCRIPT
chmod +x "$FIXTURE/deploy.sh"

echo '--- Test 1: staging env ---'
# Run in a clean subshell with explicit vars
(
  export DEPLOY_ENV=staging
  export CONFIG_DIR="$FIXTURE/config"
  "$FIXTURE/deploy.sh"
)

echo '--- Test 2: production env ---'
(
  export DEPLOY_ENV=production
  export CONFIG_DIR=/etc/prod-config
  "$FIXTURE/deploy.sh"
)

echo '--- Test 3: defaults ---'
# No env vars set — script should use its own defaults
env -i PATH="$PATH" "$FIXTURE/deploy.sh"

Pengenalan kcov untuk Cakupan Bash

Cakupan menjawab pertanyaan: baris (dan cabang) mana dari skrip saya yang benar-benar dijalankan oleh pengujian? Angka cakupan yang tinggi tidak menjamin kebenaran, tetapi angka yang rendah menunjukkan jalur yang belum diuji dan kemungkinan menyimpan bug.

Alat utama untuk cakupan Bash adalah kcov. Alat ini bekerja dengan menginstrumentasikan skrip pada tingkat OS menggunakan PTRACE (Linux) atau dtrace (macOS), sehingga tidak memerlukan perubahan pada kode sumber Anda. Alat ini menghasilkan laporan HTML yang menampilkan baris merah (belum tercakup) dan hijau (tercakup).

Penggunaan dasar:

  • kcov --include-path=./src coverage-out/ ./src/myscript.sh
  • Buka coverage-out/index.html di peramban untuk memeriksa hasil
  • Dalam CI, uraikan coverage-out/myscript.sh/coverage.json untuk mendapatkan persentase yang dapat dibaca mesin

Catatan: kcov harus dipasang secara terpisah (brew install kcov di macOS, apt install kcov di Ubuntu 20.04+).

Menjalankan kcov pada Skrip Nyata

Berikut contoh menyeluruh yang menunjukkan skrip yang siap diterapkan, pengujian yang menjalankannya, serta pemanggilan kcov untuk mengukur cakupan. Perhatikan bahwa direktori keluaran dibuat untuk setiap pengujian, sehingga hasil dari beberapa pelaksanaan pengujian dapat digabungkan nanti.

#!/usr/bin/env bash
# This demo shows the *structure* of a kcov workflow.
# It will not run kcov itself (not guaranteed to be installed),
# but the script under test and test runner are fully runnable.
set -euo pipefail

FIXTURE=$(mktemp -d)
trap 'rm -rf "$FIXTURE"' EXIT

# 1. Script under test
cat > "$FIXTURE/process.sh" << 'SCRIPT'
#!/usr/bin/env bash
set -euo pipefail
INPUT="$1"
if [[ ! -f "$INPUT" ]]; then
  echo "ERROR: file not found" >&2
  exit 1
fi
LINE_COUNT=$(wc -l < "$INPUT")
if (( LINE_COUNT == 0 )); then
  echo "WARNING: file is empty"
else
  echo "Processed $LINE_COUNT lines"
fi
SCRIPT
chmod +x "$FIXTURE/process.sh"

# 2. Happy path test (exercises line 6 and 11)
echo -e 'one\ntwo\nthree' > "$FIXTURE/data.txt"
OUT=$("$FIXTURE/process.sh" "$FIXTURE/data.txt")
echo "Happy path: $OUT"

# 3. Empty file test (exercises line 9)
touch "$FIXTURE/empty.txt"
OUT=$("$FIXTURE/process.sh" "$FIXTURE/empty.txt")
echo "Empty file: $OUT"

# 4. Missing file test (exercises line 5-6)
if ! OUT=$("$FIXTURE/process.sh" "$FIXTURE/missing.txt" 2>&1); then
  echo "Missing file (expected error): $OUT"
fi

# To measure coverage, wrap each call with kcov:
# kcov --include-path="$FIXTURE" "$FIXTURE/cov/happy" "$FIXTURE/process.sh" ...
# kcov --merge "$FIXTURE/cov/all" "$FIXTURE/cov/happy" "$FIXTURE/cov/empty"

Menggabungkan Cakupan dari Beberapa Pelaksanaan Pengujian

Satu pengujian jarang mencakup semua cabang. Anda menjalankan beberapa pengujian—masing-masing di direktori keluaran cakupannya sendiri—lalu menggabungkannya. Flag --merge milik kcov menggabungkan beberapa pelaksanaan menjadi satu laporan terpadu.

Pola umum dalam pipeline CI:

  • Jalankan pengujian A → keluaran ke cov/test_a/
  • Jalankan pengujian B → keluaran ke cov/test_b/
  • Gabungkan → kcov --merge cov/all/ cov/test_a/ cov/test_b/
  • Uraikan cov/all/<script>/coverage.json untuk mendapatkan persentase akhir

Anda juga dapat menetapkan ambang minimum dan menggagalkan build CI jika cakupan turun di bawahnya:

#!/usr/bin/env bash
# extract_coverage.sh — parse kcov JSON and fail below threshold
set -euo pipefail

MIN_COVERAGE=80   # percent
COV_JSON="${1:-coverage-out/myscript.sh/coverage.json}"

if [[ ! -f "$COV_JSON" ]]; then
  echo "ERROR: coverage JSON not found at $COV_JSON" >&2
  exit 1
fi

# kcov JSON contains a key like: "percent_covered": "87.50"
PERCENT=$(grep -oP '"percent_covered":\s*"\K[0-9.]+' "$COV_JSON")
PERCENT_INT=${PERCENT%%.*}   # truncate decimal

echo "Coverage: ${PERCENT}%  (minimum: ${MIN_COVERAGE}%)"

if (( PERCENT_INT < MIN_COVERAGE )); then
  echo "FAIL: coverage ${PERCENT}% is below threshold ${MIN_COVERAGE}%" >&2
  exit 1
fi

echo "PASS: coverage threshold met"

Cakupan Cabang vs Cakupan Baris

Ada dua metrik cakupan utama yang akan Anda temui:

  • Cakupan baris—apakah baris ini dijalankan sama sekali? Metrik ini mudah dimanipulasi: satu pengujian dapat menyentuh banyak baris, tetapi melewatkan jalur kondisional yang penting.
  • Cakupan cabang—apakah setiap cabang dari setiap if, case, dan &&/|| diambil? Sinyalnya jauh lebih kuat. Diperlukan pengujian untuk sisi benar dan salah dari setiap keputusan.

kcov melaporkan keduanya. Wawasan utamanya: cakupan baris 100% tidak berarti cakupan cabang 100%. Perhatikan skrip ini—satu pengujian dengan file yang tidak kosong akan mencakup setiap baris, tetapi cabang file kosong (baris 9 di bawah) tidak pernah tercapai:

#!/usr/bin/env bash
# Illustrates line vs branch coverage gap
set -euo pipefail

check_file() {
  local f="$1"
  if [[ -f "$f" ]]; then        # branch A (true) OR branch B (false)
    local lines
    lines=$(wc -l < "$f")
    if (( lines > 0 )); then    # branch C (true) OR branch D (false)
      echo "File has $lines lines"
    else
      echo "File is empty"     # branch D — unreached if only tested with non-empty file
    fi
  else
    echo "File missing"        # branch B — unreached if only tested with existing file
  fi
}

# Only one test: covers lines 5-10 (4 of 6 branches)
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
echo 'data' > "$TMPDIR/sample.txt"
check_file "$TMPDIR/sample.txt"

# To reach 100% branch coverage you also need:
# check_file "/nonexistent/path"
# check_file "$TMPDIR/empty.txt" (after: touch "$TMPDIR/empty.txt")

Mengintegrasikan Fixture dan Cakupan dalam CI

Menyatukan semuanya: pipeline CI yang tangguh untuk proyek Bash menggabungkan penyiapan fixture, pelaksanaan pengujian dengan kcov, penggabungan, dan pemeriksaan ambang batas dalam satu skrip. Skrip ini menjadi titik masuk CI—satu perintah untuk menjalankan semuanya.

Prinsip desain untuk pelaksana pengujian CI:

  • Setiap kasus pengujian memanggil setup_fixture dan mendaftarkan teardown_fixture melalui trap
  • Skrip yang diuji dipanggil dengan $PATH palsu dan variabel lingkungan dengan cakupan terbatas
  • kcov membungkus setiap pemanggilan dan menulis hasilnya ke subdirektori bernomor
  • Setelah semua pengujian selesai, kcov menggabungkan hasilnya dan skrip ambang batas menentukan apakah build boleh dilanjutkan
  • Pelaksana CI keluar dengan kode selain nol jika ada pengujian atau pemeriksaan cakupan yang gagal
#!/usr/bin/env bash
# ci_runner.sh — full fixture + coverage pipeline entry point
set -euo pipefail

SRC="./src/deploy.sh"
COV_ROOT="$(mktemp -d)/coverage"
trap 'rm -rf "$COV_ROOT"' EXIT
mkdir -p "$COV_ROOT"

PASS=0
FAIL=0
RUN_NUM=0

run_test() {
  local name="$1" test_fn="$2"
  local fixture
  fixture=$(mktemp -d)
  local cov_out="$COV_ROOT/run_$((++RUN_NUM))"

  if (
    trap 'rm -rf "$fixture"' EXIT
    export FIXTURE="$fixture"
    # Fake bin directory shadowing real tools
    mkdir -p "$fixture/bin"
    export PATH="$fixture/bin:$PATH"
    "$test_fn" "$fixture"
  ); then
    echo "PASS: $name"
    (( PASS++ )) || true
  else
    echo "FAIL: $name"
    (( FAIL++ )) || true
  fi
}

# Example test function
test_happy_path() {
  local fx="$1"
  mkdir -p "$fx/dist"
  echo 'app.js' > "$fx/dist/index.js"
  # Would normally run: kcov "$cov_out" "$SRC" --env=staging "$fx"
  echo "[test] happy path executed in $fx"
}

run_test 'happy_path' test_happy_path

echo "Results: $PASS passed, $FAIL failed"
(( FAIL == 0 ))

Pemeriksaan Pengetahuan: Teknik PATH Palsu

Uji pemahaman Anda tentang teknik PATH palsu yang digunakan dalam fixture pengujian Bash.

Ringkasan: Fixture, Lingkungan Sementara, dan Cakupan

Pelajaran ini membahas seluruh perangkat untuk pengujian Bash yang tepercaya dan terisolasi:

  • Direktori sementara—mktemp -d bersama trap ... EXIT menjamin pembersihan otomatis, apa pun cara pengujian berakhir.
  • Struktur fixture—pasangan setup_fixture / teardown_fixture mengisi dan menghapus struktur direktori minimal yang mencerminkan input skrip nyata.
  • PATH palsu—tempatkan program pelaksana tiruan di $FIXTURE/bin/ dan tambahkan di awal $PATH untuk mencegat pemanggilan ke curl, aws, docker, atau alat eksternal apa pun tanpa menyentuh biner sistem.
  • Pembatasan cakupan lingkungan—jalankan skrip yang diuji dalam subshell (() atau env -i) agar variabel yang diubah tidak pernah merembes kembali ke pelaksana pengujian.
  • Asersi—fungsi pembantu kecil (assert_eq, assert_file_exists, assert_contains) menghasilkan keluaran lulus/gagal yang jelas dan pesan kesalahan yang bermakna.
  • Cakupan kcov—membungkus eksekusi skrip tanpa perubahan pada sumber dan menghasilkan laporan HTML serta JSON untuk cakupan baris dan percabangan.
  • Penggabungan dan ambang batas—gabungkan beberapa proses kcov dengan --merge, uraikan JSON, lalu gagalkan CI jika cakupan turun di bawah batas minimum Anda.
  • Cakupan percabangan dibandingkan cakupan baris—selalu targetkan cakupan percabangan; cakupan baris saja dapat melewatkan seluruh jalur kondisional dan memberikan rasa percaya diri yang keliru.

Dengan teknik ini, pengujian Bash Anda menjadi sama ketatnya dengan pengujian untuk bahasa terkompilasi apa pun.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Fixture, Environment Sementara, dan Cakupan” gratis?

Ya — teks lengkap “Fixture, Environment Sementara, dan Cakupan” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Linux Command Line & Bash Scripting Mastery, upgrade ke CoddyKit PRO. Kursus Linux Command Line & Bash Scripting Mastery mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Fixture, Environment Sementara, dan Cakupan”?

Bangun fixture pengujian yang terisolasi dan ukur cabang skrip mana yang benar-benar dijalankan oleh pengujian Anda. Kamu berlatih Linux Command Line & Bash Scripting Mastery dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Linux Command Line & Bash Scripting Mastery?

Tidak diperlukan pengalaman sebelumnya. Linux Command Line & Bash Scripting Mastery di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Fixture, Environment Sementara, dan Cakupan” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Linux Command Line & Bash Scripting Mastery ini?

Ya. Setiap pelajaran Linux Command Line & Bash Scripting Mastery menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Menguji Fungsi Unit dengan Bats-core
  2. Membuat Mock Perintah dan Stub Alat Eksternal
  3. Fixture, Environment Sementara, dan Cakupan
  4. Menjalankan Pengujian Shell dalam Pipeline CI
← Kembali ke Linux Command Line & Bash Scripting Mastery