テスト方針
本ドキュメントはテストの戦略を定義する。個々のテストケースはコード側に置く。 フレームワークは GUT(導入済み。設定は
.gutconfig.json)。
前提:ドメインが Node に依存しないこと
このテスト方針は、アーキテクチャ方針 の「
core/はシーンツリーに依存しない」という制約に全面的に依存している。
core/ が RefCounted だけで組まれていれば、テストはシーンをインスタンス化せずに済み、1テストあたりミリ秒単位で回る。この速度がプロトタイプの検証サイクルを決める。
core/ に Node が1つでも混入した瞬間、この前提は崩れる。それはテストの問題ではなく設計違反である。
テストの階層
単体テスト(tests/unit/)
core/ の規則を直接検証する。テストの大半をここに置く。 1テストは「状態を組み立てて Intent を1つ流し、状態とイベントを検証する」形になる。
テスト用の BalanceConfig を渡せることが重要である。 極端な値(歩 0、歩が無限、寿命上限すぐそこ、突破ライン直前、銘が寿命上限を超えたページ、両面が白紙のページだけの手札)を渡して境界を突く。
優先して書くべき対象
ルールが込み入っていて、かつ壊れると気づきにくい箇所から書く。
| 優先 | 対象 | 何を確かめるか |
|---|---|---|
| 1 | 歩の消費 | |刻 − 銘| を方向によらず消費し、足りなければプレイできないこと。ターンをまたいで持ち越さないこと |
| 1 | 発動する面の判定 | 銘 > 刻 で未来面、銘 < 刻 で過去面、銘 = 刻 で両方(1ターン1枚)。発動する側が白紙ならプレイできず、歩も刻も動かないこと |
| 1 | 跨ぎ判定 | 直前の刻と新しい刻が座を挟むこと。座の上で停止したときは跨ぎではないこと。向きによらないこと |
| 1 | Intent の増減 | 跨いだ回数だけ消え、残ったぶんだけ被弾すること。エスカレーションで個数が増えること |
| 1 | 攻撃が跨ぎから独立していること | 座を一度も跨がないターンでも、未来面の攻撃効果が通常どおり敵に入ること。跨ぎが攻撃の条件になる退行を検知する |
| 1 | 防御の計算 | ターン終了時の |刻 − 座| に比例し、方向によらないこと。持ち越されないこと |
| 1 | 破ったときの綻び/破却効果 | 方向によらず |銘 − 刻| × 綻び倍率 の綻びを負い、破却効果がラン永続であること |
| 2 | 手札の持ち越し | ターン終了時に捨て札へ送られないこと。上限に達しているときドローが発生しないこと。捨て札へ行くのはプレイされたページだけであること |
| 2 | 引き直し | 出せる札が1枚もないときだけ発火すること。1ターン1回に制限され、引き直し後も出せなければ繰り返さないこと。引き直し後もプレイ可能なページがなくペンも尽きた状態でターン終了を宣言すると敗北すること |
| 2 | 綻びの2つの軸 | 累積値が倍率と段階報酬を、そのステージ中の上昇量が突破ラインを駆動すること |
| 2 | 勝敗条件 | 2つの敗北条件がそれぞれ独立に成立すること。寿命上限への到達は敗北ではないこと |
| 2 | ステージ内特殊操作 | 各1回制限。回数の上限を上げる作用で増えること。ステージ間で持ち越されないこと |
| 3 | ページ効果 | 個々の作用と、面が持つ作用列の単体動作 |
| 3 | マップ生成 | 到達性・幅・層数の不変条件 |
統合テスト(tests/integration/)
Intent の列を最初から最後まで流し、状態とイベント列を検証する。
ゴールデンテスト
シードと Intent 列を固定すると、出力(イベント列)は完全に決定論的になる。
これを利用して、「同じ入力から同じイベント列が出る」ことを回帰テストにする。 ルールを変更したときに、意図しない副作用がイベント列の差分として現れる。
セーブ/ロードのラウンドトリップテスト
これは必ず書くこと。 アーキテクチャ方針 契約3 の中核が守られているかを検証する唯一の手段である。
func test_保存して復元しても同じイベント列が観測できる() -> void:
var a := _start_run(seed = 12345)
_apply_some_intents(a)
var saved := a.to_dict()
var b = RunState.from_dict(saved)
var rest := _more_intents()
var events_a := _apply_all(a, rest)
var events_b := _apply_all(b, rest)
assert_eq(events_a, events_b) # ← RNG の state を保存し忘れていれば必ず落ちる
このテストが通ることが、「保存前と見分けがつかない」の定義である。
手動確認
テストで代替できないものだけを手動で確認する。
| 対象 | 理由 |
|---|---|
| 演出のタイミング・手触り | 自動化する価値が薄い |
| 数直線UIの読みやすさ | 主観評価 |
| バランスの体感 | バランス の仮説検証 |
手動確認は開発者モードから行う。 任意の状態からの開始とシード指定により、確認したい状況を直接作れる(セーブ/開発者モード)。
書かないテスト
| 対象 | 理由 |
|---|---|
| 表現層のノード構造 | シーンの形は頻繁に変わる。壊れやすく価値が低い |
.tres の中身(バランス値) | データは変わるもの。テストで固定すると調整の邪魔になる |
| Godot エンジン自体の挙動 |
ただし、.tres の「構造的な妥当性」は検証してよい(全ページが未来面と過去面の両方の効果を定義しているか、少なくとも一方が配布時に書かれているか、銘が値域内か、綻び倍率がレンジ内かなど)。これは ページカタログ の機械的な検査にあたる。
実行方法
# 全テスト(ヘッドレス)
godot --headless -s addons/gut/gut_cmdln.gd -gdir=res://tests/unit -gexit
設定は .gutconfig.json に置く。res://tests/(直下)は godot_ai アドオンの McpTestSuite 用に予約されているため、GUT のテストは必ず1段下に置くこと。 tests/integration/ を追加する際は dirs を更新する。
CI
ゲームコードの実装を開始する時点で、GUT のヘッドレス実行を CI に追加する。 core/ がシーンツリーに依存しない設計であるため、GUI なしの Linux ランナーで問題なく動作する。
追加のタイミング・カバレッジ計測の要否・ゴールデンテストのイベント列の保存先は、実装時に決める。