hikari テスト (hikari test) 仕様
本書は hikari test の仕様を定める。起動ディスパッチ全体は hikari-command.md、言語の意味論は language-spec.md、テストライブラリ本体の網羅仕様は std/test.md・std/assert.md を参照。
hikari のテストを hikari 自身で書く。*_test.hikari 内に test "name" {block} を並べ、assert 名前空間でアサーションする。CI やローカル開発での回帰検査を想定する。
test / assert は hikari test 専用の魔法束縛ではなく、import で取得する通常のライブラリ (std:test / std:assert) である。hikari test はそれらで組んだテストを収集・実行する専用ランナーであり、REPL・playground・hikari run からも同じ API を使える。
構文
hikari test [path...] [--name <NAME>]
pathは 0 個以上。各pathはファイルまたはディレクトリ。- ディレクトリは配下を再帰し、ファイル名が
_test.hikariで終わるものを収集する。 - ファイルはそのまま対象にする (名前が
_test.hikariで終わらなくてよい)。 - 引数省略時は cwd を対象ディレクトリとする。
- 収集されたファイルはパス昇順で評価する。
--name <NAME>(--name=<NAME>形式も可) を与えると、名前が `<NAME>` と一致するテストだけを実行する。値を伴わない--nameは引数不正 (終了コード2)。
テストライブラリ (std:test / std:assert)
テストの語彙は 2 つの標準ライブラリモジュールとして提供する。ambient 束縛ではないため、import すれば REPL・playground・hikari run を含む任意の文脈で使える。
std:assert — アサーション (状態なし)
equal / not_equal / ok / length / empty / fail を持つ純粋な名前空間。失敗時に panic (object.Error) を投げるだけで std:test に依存しない。test の外で失敗した場合は通常どおりトップレベルまで伝播する。
import assert := "std:assert" assert.equal(1 + 1, 2)
| 形 | pass 条件 | 失敗メッセージ例 |
|---|---|---|
assert.equal(actual, expected) |
actual == expected (language-spec.md §9.1。ユーザー定義 == スロットも尊重) |
assert.equal: expected 3, got 4 |
assert.not_equal(a, b) |
a == b が偽 |
assert.not_equal: expected values to differ, both 3 |
assert.ok(cond) |
cond が true (Bool、language-spec.md §9.3) |
assert.ok: expected true, got false |
assert.fail(message) |
(常に失敗) | message |
両辺・実値の表示は各値の Inspect を用いる。
std:test — テストの構築と実行 (collect-then-run)
test は receiver を持ち回らない純関数で、テストを構築するだけ (実行しない)。実行・集計は run が担う。
import [test, run] := "std:test" import assert := "std:assert" run [ test "add" { assert.equal(1 + 1, 2) } test "less" { assert.ok ("a" < "b") } ]
| 形 | 返り値 | 意味 |
|---|---|---|
test("name", {block}) / test "name" {block} |
Trial | name (String) のテストを 1 件構築する。block は必須スロット 0 個の本体付きオブジェクト ({body} 形)。実行はしない |
suite("name", [trials]) |
Trial | 子 Trial をまとめたグループを構築する (ネスト可)。実行時に名前は 親/子 でプレフィックスする |
run([trials]) |
Report | Trial 列を記述順に実行し、集計した Report を返す。出力はしない |
run の合否は各 Trial の block を呼んだ結果で決まる: panic (object.Error) なら FAIL (メッセージと位置を記録)、それ以外は PASS。1 件が失敗しても残りは継続実行する (合否境界は run にある)。group Trial は子を再帰実行する。
Report は集計値で、少なくとも次を持つ:
{
passed := Int
failed := Int
failures := [{ name := String, message := String, pos := String |}]
|}
出力・整形は行わない (printer / reporter は将来課題)。hikari test は返った Report を「出力と終了コード」の形式で整形出力する。REPL では run の返り値をそのまま参照・表示すればよい。
収集 (hikari test の top-level Trial 収集)
hikari test は各対象ファイルについて次を行う:
- 静的検査 (check.md / static-analysis.md §1) を通す。
test/suite(std:test) とassert(std:assert) をファイルスコープに供給して評価する (ゼロボイラープレート維持。REPL・playground・hikari runはこの供給を受けずimportで明示取得する — 同一意味論・入口 2 つ)。- ファイルの top-level が返す値のうち Trial であるものを記述順に収集する。
#{hikari}ブレース形式のファイル (テストファイルの通常形) では top-level の bare な式文 (test "name" {block}/suite …) が対象。#[hikari]ブラケット形式や|区切りを使うファイルでは file-root の位置スロット (language-spec.md §1.4) も同じ枠で拾う。名前束縛 (t := …) の値は収集対象にしない (ヘルパ・import は束縛として共有する)。 - 収集した Trial 列を
runで実行し、返った Report を「出力と終了コード」の形式で整形出力し、終了コードを立てる。
したがって _test.hikari は従来どおり top-level に test "name" {block} を並べるだけでよい。名前束縛 (ヘルパ定義・import した被テストコード) は従来どおり共有する。
名前フィルタ (--name)
--name <NAME> を与えると、収集した Trial を実行前に名前で枝刈りする。あるノードは、
自身の名前が `<NAME>` と一致すればその部分木ごと実行対象になる (単体テストなら 1 件、
suite なら配下すべて)。一致しない suite でも、子孫に一致があれば一致子孫だけを残して
実行する。単体テストで一致しなければ除外する。
同名が複数あればすべて実行する。一致 0 件なら 0 passed, 0 failed を出して正常終了 (0) する。
一致判定は各テストの名前の値 (test "…" に渡した文字列。エスケープ解決後) に対して行う。
分離 (isolation)
- 各テストファイルは fresh な root 環境 で評価する (
hikari <file>1 回相当)。ファイル間で状態は共有しない。 - ファイル評価はまず全 top-level 文を評価して Trial (thunk) を構築し、そのあと `run` が各 Trial の `block` を実行する。したがって top-level の副作用はテスト body より前に一度だけ起こり、各テストは共有 top-level 束縛 (ヘルパ定義・
importした被テストコード) を参照する。テスト間の自動状態リセットは行わないため、共有された mutable 状態を変更すると後続テストに影響しうる。 - 被テストコードは
import <対象> := "<rel>"で取り込む (hikari-command.md §5.3 と同じ caller ディレクトリ基準の解決)。被テストコード側も必要ならimport assert := "std:assert"してアサートヘルパを共有できる。
出力と終了コード
hikari test は run の返した Report をもとに、各ファイルについてファイル名見出しを出し、テストごとに PASS / FAIL 行 (FAIL は失敗メッセージと位置を続ける) を出力。最後に集計行 <P> passed, <F> failed を出す。出力例:
foo_test.hikari PASS add は和を返す FAIL add は0で恒等 assert.equal: expected 5, got 6 (foo_test.hikari:8:3) 3 passed, 1 failed
| 状況 | code |
|---|---|
| 全テスト pass (対象 0 件を含む) | 0 |
| 1 件でも FAIL / 構文・評価エラー | 1 |
| I/O エラー / 引数不正 | 2 |