Bats-core로 함수 단위 테스트
테스트 파일, 검증문 및 설정/해제를 구성하여 개별 Bash 함수를 검증합니다.
Bats-core로 함수 단위 테스트은(는) CoddyKit의 무료 Linux Command Line & Bash Scripting Mastery 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Linux Command Line & Bash Scripting Mastery 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Linux Command Line & Bash Scripting Mastery 강의에는 총 4개의 강의가 포함되어 있습니다.
Bats-core란 무엇이며 왜 사용하나요?
Bats-core(Bash 자동화 테스트 시스템)은 Bash를 위한 사실상의 단위 테스트 프레임워크입니다. 셸 함수와 스크립트에 대해 구조화되고 반복 가능한 테스트를 작성할 수 있으며, Java에서 JUnit을 사용하거나 Python에서 pytest를 사용하는 것과 같은 방식입니다.
- 각 테스트는 사람이 읽을 수 있는 설명이 포함된
@test블록입니다. - 내부의 모든 명령이 종료 코드
0을 반환하면 테스트가 통과합니다. - 처음으로 0이 아닌 종료 코드가 반환되거나 단언이 실패하면 테스트가 실패합니다.
- 출력은 TAP과 호환되므로 CI 시스템(GitHub Actions, Jenkins, GitLab CI)이 기본적으로 이해할 수 있습니다.
패키지 관리자를 사용해 설치하거나 저장소를 복제하세요.
# 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첫 번째 Bats 테스트 파일
Bats 테스트 파일의 확장자는 .bats이며 특수한 셔뱅으로 시작합니다. 핵심 구성 요소는 설명 문자열과 명령 블록이 뒤따르는 @test 지시어입니다.
- 셔뱅
#!/usr/bin/env bats는 셸에 이 파일을 실행하는 방법을 알려 줍니다. - 각
@test블록은 독립적인 테스트 사례입니다. bats my_tests.bats로 단일 파일을 실행하거나bats test/로 전체 디렉터리를 실행할 수 있습니다.
다음은 Bats 테스트 파일의 최소 구조입니다.
#!/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
}'load'로 테스트할 함수 불러오기
실제 프로젝트에서는 Bash 함수가 테스트 파일 안이 아니라 라이브러리 파일에 들어 있습니다. Bats는 테스트 파일 디렉터리를 기준으로 외부 파일을 소싱하는 load 도우미를 제공합니다.
load '../lib/math.sh'는 각 테스트가 실행되기 전에 파일을 소싱합니다.- 불러온 후에는 해당 파일에 정의된 모든 함수를 테스트 블록에서 사용할 수 있습니다.
- 깔끔하게 분리하려면 라이브러리 함수는
lib/디렉터리에, 테스트는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" ]
}핵심 단언: run, $status, $output
run 명령은 Bats 테스트의 핵심입니다. 명령을 직접 실행하는 대신 run으로 감싸면 종료 코드와 출력이 캡처되며, 테스트가 즉시 실패하지 않습니다.
$status— 마지막run명령의 종료 코드를 담습니다.$output— 마지막run명령의 통합 표준 출력을 담습니다.$lines— 출력의 각 줄을 요소로 갖는 배열입니다(${lines[0]},${lines[1]}등).
이를 통해 성공 사례와 실패 사례를 모두 단언할 수 있습니다.
#!/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"* ]]
}표현력 있는 단언에 bats-assert 사용하기
내장된 [ ] 단언도 작동하지만 실패 메시지가 충분히 자세하지 않습니다. bats-assert 도우미 라이브러리는 무엇이 잘못되었는지 정확히 출력하는 표현력 있는 단언 함수를 제공합니다.
assert_success—$status가 0인지 단언합니다.assert_failure—$status가 0이 아닌지 단언합니다.assert_output—$output이 주어진 문자열과 같은지 단언합니다.assert_output --partial— 출력에 부분 문자열이 포함되어 있는지 단언합니다.refute_output --partial— 출력에 부분 문자열이 포함되어 있지 않은지 단언합니다.
bats-core/bats-assert를 test/helpers/ 폴더에 복제한 다음 불러와 설치하세요.
#!/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 및 teardown: 테스트 수명 주기 후크
Bats는 각 테스트 전후에 자동으로 실행되는 두 특수 함수, setup과 teardown을 제공합니다. 공유 상태를 준비하고 정리하는 데 사용하면 각 테스트가 알려진 환경에서 시작할 수 있습니다.
setup()은 각 개별@test블록 전에 실행됩니다.teardown()은 각 개별@test블록 후에 실행되며, 테스트가 실패한 경우에도 실행됩니다.- 일반적인 용도는 임시 디렉터리 생성, 환경 변수 설정, 테스트 후 임시 파일 제거입니다.
#!/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 및 teardown_file: 테스트 모음 수준 후크
때로는 모든 테스트 전에 매번 설정하는 대신 비용이 큰 자원을 파일마다 한 번만 설정하면 됩니다. Bats는 이를 위해 setup_file과 teardown_file을 제공합니다.
setup_file()은 파일의 모든 테스트 전에 한 번 실행됩니다.teardown_file()은 파일의 모든 테스트 후에 한 번 실행됩니다.BATS_FILE_TMPDIR(자동으로 사용할 수 있음)을 사용해setup_file과 테스트 사이에서 데이터를 공유하세요. 일반 변수는 서브셸 간에 유지되지 않습니다.
일반적인 사용 사례는 모의 서버를 시작하거나 바이너리를 한 번 빌드한 다음 마지막에 종료하는 것입니다.
#!/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"
}파일을 수정하는 함수 테스트하기
매우 흔한 패턴은 파일 시스템에서 읽거나 파일 시스템에 쓰는 Bash 함수를 테스트하는 것입니다. 핵심 기법은 setup에서 mktemp -d를 사용해 임시 디렉터리를 만드는 것입니다. 이렇게 하면 테스트가 실제 파일을 건드리지 않고 서로 간섭하지도 않습니다.
- 항상
$TEST_DIR안에서 작업하세요(또는 최신 Bats에서 자동으로 사용할 수 있는$BATS_TEST_TMPDIR을 사용하세요). assert_file_exists와assert_file_contains처럼 파일에 대해 깔끔하게 단언하려면bats-file도우미 라이브러리를 사용하세요./tmp/myfile같은 경로를 하드코딩하지 마세요. 병렬 테스트 실행이 서로 충돌합니다.
#!/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"
}외부 명령 모의 처리하기
함수는 curl, aws, git 같은 외부 프로그램을 호출하는 경우가 많습니다. 단위 테스트에서는 실제 외부 명령이 아니라 여러분의 로직을 테스트해야 합니다. Bats에서 가장 깔끔한 모의 처리 기법은 setup 안에서 명령과 같은 이름의 셸 함수를 정의하는 것입니다. 그러면 실제 바이너리보다 이 함수가 우선됩니다.
curl() { echo 'mocked response'; return 0; }같은 함수를setup에서 정의하고 내보내세요.export -f curl을 사용하면run이 생성하는 서브셸에서도 함수를 볼 수 있습니다.- 더 복잡한 상황에서는 모의를
PATH의 임시 파일에 작성할 수도 있습니다.
#!/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"
}테스트 건너뛰기와 태그 지정
모든 테스트를 항상 실행할 수 있는 것은 아닙니다. 실제 네트워크 연결, 특정 도구 또는 특정 OS가 필요한 경우가 있습니다. Bats는 테스트를 주석 처리하거나 테스트 모음을 중단하는 대신, 안내 메시지와 함께 조건부로 테스트를 건너뛰는 skip을 제공합니다.
@test블록 안 어디에서든skip "reason"을 호출하면 해당 테스트를 건너뜁니다.- 건너뛴 테스트는 출력에
S로 표시되며 실패로 집계되지 않습니다. - Bats 1.5 이상은 태그를 지원합니다.
# bats test_tags=slow,network으로 테스트에 주석을 달고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/완전한 테스트 모음 구성하기
잘 구성된 Bats 프로젝트는 예측 가능한 디렉터리 배치를 따르므로 새로운 기여자를 쉽게 참여시키고 CI 파이프라인과 통합할 수 있습니다.
권장 구조는 다음과 같습니다.
lib/— 운영 환경용 Bash 함수(관심사별로 파일 하나씩:math.sh,fileutils.sh).test/— 라이브러리 파일마다.bats파일 하나씩(math.bats,fileutils.bats).test/helpers/— bats-assert, bats-file, bats-support를 git 서브모듈로 둡니다.Makefile— 기여자가make test만 실행할 수 있도록test대상을 제공합니다.
다음 한 명령으로 전체 테스트 모음을 실행하세요.
# 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지식 점검: Bats-core 단언
Bats-core의 핵심 테스트 메커니즘을 얼마나 이해했는지 확인해 보세요.
복습: Bats-core로 Bash 단위 테스트하기
이 과에서는 Bats-core를 사용해 개별 Bash 함수의 단위 테스트를 구성하고 작성하는 방법을 배웠습니다. 핵심 내용은 다음과 같습니다.
- 테스트 파일 구조 —
#!/usr/bin/env bats셔뱅과 설명이 포함된 이름의@test블록을 사용합니다. load— 라이브러리 파일을 소싱하여 복사해 붙여 넣지 않고도 테스트에서 함수를 사용할 수 있게 합니다.run+$status+$output— 핵심 세 요소입니다. 즉시 테스트를 실패시키지 않고 결과를 캡처하려면 항상run을 사용하세요.- bats-assert — 읽기 쉬운 실패 메시지를 위해 원시
[ ]보다assert_success,assert_failure,assert_output을 우선 사용하세요. setup/teardown— 각 테스트 전후에 실행되며,setup_file/teardown_file은 파일마다 한 번 실행됩니다.- 모의 처리 — 외부 명령을 같은 이름의 셸 함수로 가리고
export -f로 내보냅니다. skip— 사용할 수 없는 자원에 의존하는 테스트를 조건부로 건너뜁니다.- 프로젝트 배치 — 유지 관리와 CI 통합을 위해
lib/,test/,test/helpers/를 분리해 두세요.
이러한 패턴을 사용하면 Bash 프로젝트에도 현대적인 소프트웨어 프로젝트에 적용하는 것과 같은 테스트 규율을 갖출 수 있습니다.
자주 묻는 질문
“Bats-core로 함수 단위 테스트” 강의는 무료인가요?
네 — “Bats-core로 함수 단위 테스트” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Linux Command Line & Bash Scripting Mastery 강의 전체를 잠금 해제할 수 있습니다. Linux Command Line & Bash Scripting Mastery 강의에는 총 4개의 강의가 포함되어 있습니다.
“Bats-core로 함수 단위 테스트”에서 뭘 배우나요?
테스트 파일, 검증문 및 설정/해제를 구성하여 개별 Bash 함수를 검증합니다. 브라우저에서 직접 실행하는 실습 코드로 Linux Command Line & Bash Scripting Mastery을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Linux Command Line & Bash Scripting Mastery을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Linux Command Line & Bash Scripting Mastery은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“Bats-core로 함수 단위 테스트” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Linux Command Line & Bash Scripting Mastery 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Linux Command Line & Bash Scripting Mastery 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- Bats-core로 함수 단위 테스트
- 명령 모킹 및 외부 도구 스텁 처리
- 테스트 픽스처, 임시 환경 및 커버리지
- CI 파이프라인에서 셸 테스트 실행