테스트 프레임워크
테스트 프레임워크
섹션 제목: “테스트 프레임워크”wippy/test 모듈은 어서션, 생명주기 훅, 모킹을 갖춘 BDD 스타일 테스트 프레임워크를 제공합니다.
의존성을 추가합니다:
wippy add wippy/testwippy install모듈은 test 명령을 자동으로 등록합니다. 설치 후 wippy run test로 프로젝트의 모든 테스트 엔트리를 검색하고 실행합니다.
테스트 정의하기
섹션 제목: “테스트 정의하기”테스트는 meta.type: test를 가진 function.lua 엔트리입니다:
version: "1.0"namespace: app.test
entries: - name: math kind: function.lua meta: type: test suite: math name: 수학 연산 source: file://math_test.lua method: run imports: test: wippy.test:test테스트 메타데이터
섹션 제목: “테스트 메타데이터”| Field | Required | Description |
|---|---|---|
type | Yes | 러너가 검색하려면 "test"여야 합니다 |
suite | No | 러너 출력에서 테스트를 그룹화합니다 |
name | No | 러너 출력에 표시되는 이름 |
order | No | 스위트 내 정렬 순서 (낮을수록 먼저 실행) |
테스트 작성하기
섹션 제목: “테스트 작성하기”BDD 스타일
섹션 제목: “BDD 스타일”describe와 it 블록을 사용하여 테스트를 구조화합니다:
local test = require("test")
local function define_tests() test.describe("calculator", function() test.it("adds numbers", function() test.eq(1 + 1, 2) end)
test.it("multiplies numbers", function() test.eq(3 * 4, 12) end) end)end
local run_cases = test.run_cases(define_tests)
local function run(options) local result = run_cases(options) if result.failed_tests > 0 then error("tests failed: " .. result.failed_tests) end return resultend
return { run = run }중첩 스위트
섹션 제목: “중첩 스위트”스위트를 중첩하여 구성할 수 있습니다:
test.describe("user", function() test.describe("validation", function() test.it("requires name", function() test.ok(validate({}).error) end)
test.it("accepts valid input", function() test.is_nil(validate({name = "Alice"}).error) end) end)
test.describe("formatting", function() test.it("formats display name", function() test.eq(format_name("alice"), "Alice") end) end)end)테스트 건너뛰기
섹션 제목: “테스트 건너뛰기”test.it_skip("not implemented yet", function() test.fail("TODO")end)건너뛴 테스트는 출력에 표시되지만 실패로 집계되지 않습니다.
스위트 별칭
섹션 제목: “스위트 별칭”test.spec과 test.context는 test.describe의 별칭입니다:
test.spec("feature", function() test.context("when valid input", function() test.it("succeeds", function() test.ok(true) end) end)end)어서션
섹션 제목: “어서션”동등성
섹션 제목: “동등성”test.eq(actual, expected, msg?) -- actual == expectedtest.neq(actual, expected, msg?) -- actual ~= expected참/거짓 판별
섹션 제목: “참/거짓 판별”test.ok(val, msg?) -- val is truthytest.fail(msg?) -- unconditional failureNil 검사
섹션 제목: “Nil 검사”test.is_nil(val, msg?) -- val == niltest.not_nil(val, msg?) -- val ~= nil타입 검사
섹션 제목: “타입 검사”test.is_true(val, msg?) -- val == truetest.is_false(val, msg?) -- val == falsetest.is_string(val, msg?)test.is_number(val, msg?)test.is_table(val, msg?)test.is_function(val, msg?)test.is_boolean(val, msg?)문자열 및 컬렉션
섹션 제목: “문자열 및 컬렉션”test.contains(str, substr, msg?) -- substring matchtest.matches(str, pattern, msg?) -- Lua pattern matchtest.has_key(tbl, key, msg?) -- table key existstest.len(val, expected, msg?) -- #val == expected수치 비교
섹션 제목: “수치 비교”test.gt(a, b, msg?) -- a > btest.gte(a, b, msg?) -- a >= btest.lt(a, b, msg?) -- a < btest.lte(a, b, msg?) -- a <= b오류 처리
섹션 제목: “오류 처리”test.throws(fn, msg?) -- fn() raises error, returns ittest.has_error(val, err, msg?) -- val is nil, err is not niltest.no_error(val, err, msg?) -- err is nil모든 어서션은 마지막 인자로 선택적 메시지를 받습니다. 실패 시 해당 메시지가 오류 출력에 포함됩니다.
생명주기 훅
섹션 제목: “생명주기 훅”test.describe("database", function() test.before_all(function() -- runs once before the suite db = connect() end)
test.after_all(function() -- runs once after the suite db:close() end)
test.before_each(function() -- runs before each test db:begin_transaction() end)
test.after_each(function() -- runs after each test db:rollback() end)
test.it("inserts a record", function() db:exec("INSERT INTO users (name) VALUES ('Alice')") local count = db:query_row("SELECT COUNT(*) FROM users") test.eq(count, 1) end)end)중첩 스위트의 훅은 순서대로 실행됩니다: 부모 before_each가 자식 before_each 전에 실행되고, 자식 after_each가 부모 after_each 전에 실행됩니다.
모킹 시스템은 전역 객체 필드를 교체하고 각 테스트 후 자동으로 복원합니다.
기본 모킹
섹션 제목: “기본 모킹”test.describe("notifications", function() test.it("sends message", function() local sent = false test.mock("process.send", function(pid, topic, payload) sent = true end)
notify_user("hello") test.is_true(sent) -- mock is auto-restored after this test end)end)모킹 API
섹션 제목: “모킹 API”test.mock("object.field", replacement) -- replace a global fieldtest.mock_process("field", replacement) -- shorthand for process fieldstest.restore_mock("object.field") -- restore one mocktest.restore_all_mocks() -- restore all mocks모킹 경로는 점 표기법을 사용합니다: "process.send"는 _G.process.send를 교체합니다.
process.send에 대한 모킹은 원래 함수를 통해 테스트 프레임워크 메시지를 자동으로 프록시하므로, process.send가 모킹된 상태에서도 테스트 이벤트 리포팅이 계속 동작합니다.
모든 모킹은 after_each 훅을 통해 각 테스트 후 자동으로 복원됩니다.
테스트 실행하기
섹션 제목: “테스트 실행하기”전체 테스트 실행
섹션 제목: “전체 테스트 실행”wippy run test패턴으로 필터링
섹션 제목: “패턴으로 필터링”wippy run test mathwippy run test user validation필터는 엔트리 ID와 매칭됩니다. 여러 패턴이 결합됩니다.
출력 예시
섹션 제목: “출력 예시”3 tests in 1 suites
calculator + adds numbers 0ms + multiplies numbers 0ms - divides by zero 1ms Error: expected error, got nil
1 suite | 2 passed | 1 failed | 0 skipped | 3ms간단한 테스트
섹션 제목: “간단한 테스트”BDD 프레임워크가 필요 없는 테스트의 경우, true를 반환하거나 오류를 발생시키는 간단한 함수를 정의합니다:
local funcs = require("funcs")
local function main() local result, err = funcs.call("app:my_function", "input") if err then error("call failed: " .. tostring(err)) end if result ~= "expected" then error("expected 'expected', got: " .. tostring(result)) end return trueend
return { main = main } - name: integration kind: function.lua meta: type: test suite: integration source: file://integration_test.lua method: main modules: - funcs러너는 테스트가 BDD 케이스 이벤트를 사용하는지 단순 값을 반환하는지 감지합니다. 두 패턴 모두 wippy run test에서 동작합니다.
프로젝트 구조
섹션 제목: “프로젝트 구조”일반적인 테스트 레이아웃:
src/ _index.yaml app.lua test/ _index.yaml # test entries math_test.lua user_test.lua integration_test.lua테스트 _index.yaml은 테스트 네임스페이스와 엔트리를 정의합니다:
version: "1.0"namespace: app.test
entries: - name: math kind: function.lua meta: type: test suite: math source: file://math_test.lua method: run imports: test: wippy.test:test
- name: user kind: function.lua meta: type: test suite: user source: file://user_test.lua method: run imports: test: wippy.test:test인프라 요구사항
섹션 제목: “인프라 요구사항”테스트 러너는 애플리케이션에 process.host와 terminal.host가 필요합니다. 일반적으로 이미 존재합니다. 없는 경우 추가하십시오:
entries: - name: processes kind: process.host lifecycle: auto_start: true
- name: terminal kind: terminal.host lifecycle: auto_start: true