DevOps-bootcamp · Oppitunti

Funktioiden yksikkötestaus Bats-corella

Rakenna testitiedostot, väitteet ja valmistelu- sekä purkuvaiheet yksittäisten Bash-funktioiden tarkistamista varten.

Oppitunti 1/413 vaihetta

Funktioiden yksikkötestaus Bats-corella on ilmainen DevOps-bootcamp-oppitunti CoddyKitissä. Tämä on oppitunti 1/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu DevOps-bootcamp-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. DevOps-bootcamp-kurssilla on yhteensä 4 oppituntia.

Mikä Bats-core on ja miksi sitä käytetään?

Bats-core (Bash Automated Testing System) on Bashin de facto -yksikkötestauskehys. Sen avulla voitte kirjoittaa jäsenneltyjä ja toistettavia testejä shell-funktioille ja -skripteille samalla tavalla kuin käyttäisitte JUnitia Javassa tai pythoniin tarkoitettua pytestiä.

  • Jokainen testi on @test-lohko, jolla on ihmiselle helposti luettava kuvaus.
  • Testi läpäistään, kun jokainen sen sisällä suoritettava komento palauttaa paluuarvon 0.
  • Testi epäonnistuu ensimmäisen nollasta poikkeavan paluuarvon tai epäonnistuneen väitteen kohdalla.
  • Tuloste on TAP-yhteensopiva, joten CI-järjestelmät (GitHub Actions, Jenkins, GitLab CI) ymmärtävät sen suoraan.

Asentakaa se paketinhallinnan avulla tai kloonatkaa repositorio:

# 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

Ensimmäinen Bats-testitiedostonne

Bats-testitiedoston tiedostopääte on .bats, ja se alkaa erityisellä shebang-rivillä. Keskeinen rakennuspalikka on @test-direktiivi, jota seuraavat kuvausmerkkijono ja komentolohko.

  • Shebang #!/usr/bin/env bats kertoo shellille, miten tiedosto suoritetaan.
  • Jokainen @test-lohko on itsenäinen testitapaus.
  • Voitte suorittaa yksittäisen tiedoston komennolla bats my_tests.bats tai kokonaisen hakemiston komennolla bats test/.

Alla on Bats-testitiedoston vähimmäisrakenne:

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

Testattavan funktion lataaminen load-komennolla

Oikeissa projekteissa Bash-funktiot sijaitsevat kirjastotiedostoissa, eivät itse testitiedostossa. Bats tarjoaa load-apufunktion, jolla ulkoiset tiedostot voidaan lähdekoodata testitiedoston hakemistoon nähden suhteellisella polulla.

  • load '../lib/math.sh' lähdekoodaa tiedoston ennen jokaisen testin suorittamista.
  • Lataamisen jälkeen kaikki kyseisessä tiedostossa määritellyt funktiot ovat käytettävissä testilohkoissa.
  • Selkeän erottelun vuoksi pitäkää kirjastofunktiot lib/-hakemistossa ja testit test/-hakemistossa.

Esimerkki projektin rakenteesta ja sitä vastaavasta testistä:

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

Keskeiset väitteet: run, $status, $output

run-komento on Bats-testauksen ydin. Sen sijaan että suorittaisitte komennon suoraan, ympäröikää se run-komennolla. Tällöin sen paluuarvo ja tuloste tallennetaan ilman, että testi epäonnistuu heti.

  • $status — sisältää viimeisimmän run-komennon paluuarvon.
  • $output — sisältää viimeisimmän run-komennon yhdistetyn vakiotulosteen.
  • $lines — taulukko, jonka jokainen alkio on yksi tulosterivi (${lines[0]}, ${lines[1]} jne.).

Näin voitte tarkistaa sekä onnistuvat että epäonnistuvat tilanteet:

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

Ilmaisuvoimaisten väitteiden käyttäminen bats-assert-kirjastolla

Sisäänrakennetut [ ]-väitteet toimivat, mutta niiden virheilmoitukset ovat niukkoja. bats-assert-apukirjasto tarjoaa ilmaisuvoimaisia väitefunktioita, jotka ilmoittavat tarkasti, mikä meni vikaan.

  • assert_success — varmistaa, että $status on 0.
  • assert_failure — varmistaa, että $status poikkeaa nollasta.
  • assert_output — varmistaa, että $output vastaa annettua merkkijonoa.
  • assert_output --partial — varmistaa, että tuloste sisältää osamerkkijonon.
  • refute_output --partial — varmistaa, että tuloste EI sisällä osamerkkijonoa.

Asentakaa kirjasto kloonaamalla bats-core/bats-assert test/helpers/-hakemistoon ja lataamalla se sitten:

#!/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 ja teardown: testin elinkaarikoukut

Bats tarjoaa kaksi erityistä funktiota — setup ja teardown — jotka suoritetaan automaattisesti jokaisen testin yhteydessä. Käyttäkää niitä jaetun tilan valmistelemiseen ja siivoamiseen, jotta jokainen testi alkaa tunnetusta ympäristöstä.

  • setup() suoritetaan ennen jokaista yksittäistä @test-lohkoa.
  • teardown() suoritetaan jokaisen yksittäisen @test-lohkon jälkeen, vaikka testi epäonnistuisi.
  • Yleisiä käyttötarkoituksia ovat väliaikaisten hakemistojen luominen, ympäristömuuttujien asettaminen ja väliaikaisten tiedostojen poistaminen testin jälkeen.
#!/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 ja teardown_file: testisarjan elinkaarikoukut

Joskus kallis resurssi tarvitsee valmistella vain kerran tiedostoa kohden, ei ennen jokaista testiä. Bats tarjoaa tätä varten funktiot setup_file ja teardown_file.

  • setup_file() suoritetaan kerran ennen kaikkia tiedoston testejä.
  • teardown_file() suoritetaan kerran kaikkien tiedoston testien jälkeen.
  • Käyttäkää BATS_FILE_TMPDIR-muuttujaa (saatavilla automaattisesti) tietojen jakamiseen setup_file-funktion ja testien välillä — tavalliset muuttujat eivät säily alikuorien välillä.

Tyypillinen käyttötapaus on valetun palvelimen käynnistäminen tai binäärin rakentaminen kerran ja sen sulkeminen lopuksi:

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

Tiedostoja muokkaavien funktioiden testaaminen

Hyvin yleinen toimintamalli on testata Bash-funktioita, jotka lukevat tiedostojärjestelmästä tai kirjoittavat siihen. Keskeinen tekniikka on käyttää väliaikaisia hakemistoja (luomalla ne komennolla mktemp -d setup-funktiossa), jotta testit eivät koskaan käsittele oikeita tiedostoja eivätkä häiritse toisiaan.

  • Työskennelkää aina hakemiston $TEST_DIR sisällä (tai $BATS_TEST_TMPDIR-hakemistossa, joka on saatavilla automaattisesti Batsin uusissa versioissa).
  • Käyttäkää bats-file-apukirjastoa selkeisiin tiedostoväitteisiin, kuten assert_file_exists ja assert_file_contains.
  • Älkää koskaan määrittäkö polkuja kiinteästi, esimerkiksi /tmp/myfile, sillä rinnakkaiset testiajot törmäävät toisiinsa.
#!/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"
}

Ulkoisten komentojen valeet

Funktiot kutsuvat usein ulkoisia ohjelmia, kuten curl, aws tai git. Yksikkötesteissä haluatte testata omaa logiikkaanne, ette oikeaa ulkoista komentoa. Selkein tapa toteuttaa mockaus Batsissa on määrittää setup-funktiossa komentoa samanniminen shell-funktio — se ohittaa oikean binäärin.

  • Määrittäkää setup-funktiossa esimerkiksi funktio curl() { echo 'mocked response'; return 0; } ja viekää se ympäristöön.
  • Käyttäkää komentoa export -f curl, jotta funktio näkyy komennon run luomissa alikuorissa.
  • Monimutkaisemmissa tilanteissa voitte myös kirjoittaa valeen väliaikaiseen tiedostoon, joka sijaitsee PATH-muuttujan hakemistossa.
#!/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"
}

Testien ohittaminen ja tagien käyttö

Kaikkia testejä ei aina voi suorittaa — joskus tarvitaan oikea verkkoyhteys, tietty työkalu tai tietty käyttöjärjestelmä. Bats tarjoaa skip-komennon, jolla testin voi ohittaa ehdollisesti ja informatiivisen viestin kera sen sijaan, että testi kommentoitaisiin pois tai testisarja rikottaisiin.

  • Kutsukaa skip "reason" missä tahansa @test-lohkon sisällä ohittaaksenne testin.
  • Ohitetut testit näkyvät tulosteessa merkintänä S, eikä niitä lasketa epäonnistumisiksi.
  • Bats 1.5+ tukee tageja: merkitkää testit annotaatiolla # bats test_tags=slow,network ja suodattakaa ne komennolla 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/

Kokonaisen testisarjan jäsentäminen

Hyvin järjestetty Bats-projekti noudattaa ennakoitavaa hakemistorakennetta, mikä helpottaa uusien osallistujien perehdyttämistä ja integrointia CI-putkiin.

Suositeltu rakenne:

  • lib/ — tuotannossa käytettävät Bash-funktiot (yksi tiedosto kutakin kokonaisuutta kohti: math.sh, fileutils.sh).
  • test/ — yksi .bats-tiedosto kutakin kirjastotiedostoa kohti (math.bats, fileutils.bats).
  • test/helpers/ — bats-assert, bats-file ja bats-support Git-alimoduuleina.
  • Makefile — test-kohde, jotta osallistujat voivat suorittaa komennon make test.

Suorittakaa koko testisarja yhdellä komennolla:

# 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

Tietotesti: Bats-core-väitteet

Testatkaa, kuinka hyvin ymmärrätte Bats-coren keskeisen testausmekanismin.

Kertaus: Bashin yksikkötestaus Bats-corella

Tässä oppitunnissa opitte jäsentämään ja kirjoittamaan yksikkötestejä yksittäisille Bash-funktioille Bats-coren avulla. Tässä ovat tärkeimmät asiat:

  • Testitiedoston rakenne — käyttäkää #!/usr/bin/env bats-shebang-riviä ja kuvaavasti nimettyjä @test-lohkoja.
  • load — lähdekoodatkaa kirjastotiedostonne, jotta funktiot ovat testeissä käytettävissä ilman kopiointia ja liittämistä.
  • run + $status + $output — keskeinen kolmikko; käyttäkää aina run-komentoa tulosten tallentamiseen ilman, että testi epäonnistuu heti.
  • bats-assert — suosikaa luettavien virheilmoitusten vuoksi funktioita assert_success, assert_failure ja assert_output raakojen [ ]-väitteiden sijaan.
  • setup / teardown — suoritetaan ennen jokaista testiä ja sen jälkeen; setup_file / teardown_file suoritetaan kerran tiedostoa kohti.
  • Mockaus — ohittakaa ulkoiset komennot samannimisillä shell-funktioilla, jotka viedään ympäristöön komennolla export -f.
  • skip — ohittakaa ehdollisesti testit, jotka riippuvat käytettävissä olemattomista resursseista.
  • Projektin rakenne — pitäkää lib/-, test/- ja test/helpers/-hakemistot erillään ylläpidettävyyden ja CI-integraation vuoksi.

Näiden toimintamallien ansiosta voitte soveltaa Bash-projekteihinne samaa testauskuria kuin mihin tahansa nykyaikaiseen ohjelmistoprojektiin.

Aloita maksutta

Opi DevOps-bootcamp tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
142
Oppitunnit
568

Usein kysytyt kysymykset

Onko oppitunti ”Funktioiden yksikkötestaus Bats-corella” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa DevOps-bootcamp-oppimispolun 3 oppituntia, myös oppitunnin “Funktioiden yksikkötestaus Bats-corella”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. DevOps-bootcamp-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Funktioiden yksikkötestaus Bats-corella”?

Rakenna testitiedostot, väitteet ja valmistelu- sekä purkuvaiheet yksittäisten Bash-funktioiden tarkistamista varten. Harjoittelet DevOps-bootcamp-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni DevOps-bootcamp-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin DevOps-bootcamp-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 1/4.

Kuinka kauan ”Funktioiden yksikkötestaus Bats-corella”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä DevOps-bootcamp-oppitunnilla?

Kyllä. Jokainen DevOps-bootcamp-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Funktioiden yksikkötestaus Bats-corella
  2. Komentojen mallintaminen ja ulkoisten työkalujen korvaaminen
  3. Testiaineistot, väliaikaisympäristöt ja kattavuus
  4. Shell-testien suorittaminen CI-putkissa
← Takaisin: DevOps-bootcamp