メインコンテンツまでスキップ

テスト方針

本ドキュメントはテストの戦略を定義する。個々のテストケースはコード側に置く。 フレームワークは GUT(導入済み。設定は .gutconfig.json)。

前提:ドメインが Node に依存しないこと​

このテスト方針は、アーキテクチャ方針 の「core/ はシーンツリーに依存しない」という制約に全面的に依存している。

core/ が RefCounted だけで組まれていれば、テストはシーンをインスタンス化せずに済み、1テストあたりミリ秒単位で回る。この速度がプロトタイプの検証サイクルを決める。

core/ に Node が1つでも混入した瞬間、この前提は崩れる。それはテストの問題ではなく設計違反である。

テストの階層​

単体テスト(tests/unit/)​

core/ の規則を直接検証する。テストの大半をここに置く。 1テストは「状態を組み立てて Intent を1つ流し、状態とイベントを検証する」形になる。

テスト用の BalanceConfig を渡せることが重要である。 極端な値(歩 0、歩が無限、寿命上限すぐそこ、突破ライン直前、銘が寿命上限を超えたページ、両面が白紙のページだけの手札)を渡して境界を突く。

優先して書くべき対象​

ルールが込み入っていて、かつ壊れると気づきにくい箇所から書く。

優先対象何を確かめるか
1歩の消費|刻 − 銘| を方向によらず消費し、足りなければプレイできないこと。ターンをまたいで持ち越さないこと
1発動する面の判定銘 > 刻 で未来面、銘 < 刻 で過去面、銘 = 刻 で両方(1ターン1枚)。発動する側が白紙ならプレイできず、歩も刻も動かないこと
1跨ぎ判定直前の刻と新しい刻が座を挟むこと。座の上で停止したときは跨ぎではないこと。向きによらないこと
1Intent の増減跨いだ回数だけ消え、残ったぶんだけ被弾すること。エスカレーションで個数が増えること
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 ランナーで問題なく動作する。

追加のタイミング・カバレッジ計測の要否・ゴールデンテストのイベント列の保存先は、実装時に決める。

関連​

Implementation Notes​