🐙

変更差分からユニットテスト/結合テスト/システムテストのテスト観点を出せるのか?【cursor】

に公開

Unityで横スクロールのシューティングゲームを開発しています。(個人の趣味)
新しい武器アイテムを追加したのでそのテストが必要となりました。
そこで考えたのは以下。
「AIで影響範囲を加味してユニットテストレベル、インテグレーションテスト(結合テスト)レベル、システムテストレベルのテスト観点を出せないか」
試してみたのでご興味ある方はご覧ください。
使用しているエディターはcursorで、モデルはclaude-4-sonnetです。

テストの基本

テストでやること3つ

テストにはおおよそ3つやることがあります。
1つめは、決めたことが決めた通り動くか確認する活動。
ユニットテストが一例。「これを入れたら、これが出力されるはず」の確認作業です。
2つめは、忘れ物(バグ)を探索する活動。
バグは何もコードの書き損じのみで発生するものではありません。
皆さんも経験があるかと思いますが、大体のバグはうっかりの忘れ物で発生します。
「あーそうだ!管理ツール側も変更しなきゃダメだったじゃん!」みたいな。
考慮漏れというやつです。
当然「これを入れたらこれが出力されるはず」という確認活動ができません。
そもそも忘れているのでコード上に現れません。
実際にシステムを触ってバグ探しをしていると発掘されます。
テストが最初に述べた確認活動に終始していると、バグを探そうとしていないので、本番でユーザーにバグを発掘されます。南無。
3つめは、妥当性確認。
要求にあったものが作れているかの確認です。「顧客が本当に必要だったもの」です。
社内ならドッグフーディング、社外ならA/Bテストや、ユーザーテストなどで確認することが多いです。

テストレベル

テストといっても、ユニットテストなの?システムを通したテストなの?ということがあります。
そんなテスト活動をグループ分けしたものがテストレベルです。
まずはユニットテストレベル(コンポーネントテスト)。
コードでのテストです。「これを入れたらこれが出力される」の確認です。
主にロジックを確認することになります。
次がインテグレーションテストレベル(結合テスト/コンポーネント統合テスト)。
メソッドとメソッドのやりとり、コンポーネント間のやり取りなど、インターフェイス(接点の部分)に着目して相互処理の確認をします。
例えばシューティングゲームの武器なら、ItemDBからダメージの値をちゃんと取ってきて反映してるよね、などです。
次がシステムテストレベル。(E2Eテスト、UIテスト……会社によって呼び方は様々)
システムを通してのテストです。手動のテストやバグ探しもよくこのレベルで行われます。
他に、システム統合テストや受け入れテストがありますが今回の話とは関係がないので割愛します。
テストを考えるとき、全てのテストをシステムテストレベルや全てをユニットテストレベルで行うのは効率が悪いです。
ロジック確認を目的としたテストはユニットテストレベルで、バグ探しはシステムテスト、データを取得できるかはインテグレーションレベルで行うなど、プロダクト全体を通してテストの目的に応じて適材適所で実施することが良かったりします。

本編

テスト対象

今回テスト対象となるアイテムは「クラスター爆弾」です。

  • ミサイルが重力に従って一回落ちる
  • そこからゴゴゴーと前に推進する
  • パカっと外装が外れて、子弾が下にばらまかれる
    上記のような振る舞いです。絵にすると以下です。

    ……ぐぬぬ。
    さて、実装後の実際の挙動は以下です。
    クラスター爆弾動画(4秒)

仕様

システムテストができるくらいの粒度で仕様を記載します。

  • ClusterBattery(射出口だと思ってくれれば)から5秒おきにClusterBombを発射
  • ClusterBombは0.5秒重力に従って落ちる
  • 発射0.5秒後、ロケットエンジンが点火、徐々に前方に向かって速度を上げる
  • 発射1秒後、外装が外れる
  • 外装が外れた後、0.05秒間隔で下方90度±30度のランダムな角度でClusterBullet(子弾)が発射され続ける
  • 子弾は画面下限+0.5mの位置で爆発する
  • 子弾が敵に直撃すると、敵は5のダメージを受ける。
  • 親(ClusterBomb)は敵に直撃すると、敵に20のダメージを与える。その場でClusterBombは爆発とともに消える
  • ダメージはItemData(ローカルDB的なモノ)から取得する
  • ClusterBombは画面端を超えると、子弾の発射を止め、自身が削除される
  • ClusterBatteryはアイテム(ItemCluster)をはじめて取った時にアクティブになる
  • 以降、ItemClusterを取るごとに、0.05秒間隔で発射される1回の子弾数が2つ、3つと増える
  • 1回に発射される子弾の上限は8つ。ItemClusterをそれ以上とっても増えることはない。
  • (アイテムは敵を倒すと10%程度の確率で出現する)

やること

Cursorはワークツリー上のdiffをコンテキストとして与えることができます。

このdiffから、ユニットテストレベルでテストすべきこと、インテグレーションテストレベルでテストすべきこと、システムテストレベルでテストすべきことを出せるか試してみます。
変更した箇所をテストするのは当たり前ですし、コードからテストを起こしたところでマッチポンプです。
なので今回は、diffには載っていないけどテストしたほうがいいテスト観点を出してもらいます。
つまりテストの基本で書いた 「忘れ物」を拾えるようなテスト観点を出せるか を試します。
今回はあえて以下のミスをしています。

  • 全てのアイテム上で必ず呼んでいるSetDamage()を呼んでいない。これによりItemDataからダメージを取ってこない。コード上にダメージをベタ書きしている。なので、他アイテムのようにItemDataをいじってもダメージが反映されない。
  • 敵は何かオブジェクトにぶつかった時、ぶつかったオブジェクトのTagで判定している。このTagがPlayerBulletだった場合ダメージ処理を行う。けど、親(ClusterBomb)はTagを設定していない。よって親がぶつかってもダメージ処理がされない(普通に忘れていた。今回の実験のため放置)

変更したファイル


追加や変更をしたソースファイルは*.csのファイルです。

  • ConfigPlayerBullet.cs
  • ClusterBatery.cs
  • ClusterChildrenBomb.cs
  • ClusterMainBomb.cs
    *.prefabのファイルは自動的に生成されるオブジェクトの設定ファイルです。

まずは自分でテストすべきことを考えておく

AIでテストすべきことを出す前に、自分でも考えてみます。

  • 仕様に書かれている動作すべて (記載略)
    • クラスター爆弾の一連の動きの正しさ
    • クラスター爆弾の一連の動きの自然さやカッコよさ(大事)
    • アイテム取得から発射、破棄までのライフサイクル etc...
  • ゲーム内特有のルールが適用されているか
    • プレイヤーが右向きから左向きになったときに発射方向が180度変わるか
      • 横シューティングなのに向きを変えれるというこのゲーム特有部分のチェック
    • 左向きに発射したときに画面端まで行ったら子弾発射を止め破棄されるか
    • プレイヤーキャラごとにダメージ倍率が変わるが反映されているか
      • ゲーム内の「当たり前」であり実装済み箇所なので、処理を呼び忘れるとコード上の変更箇所には現れず、 「ない(忘れている)ことに気づける」 人/AIでなければ気づけない
    • 他の武器と合わせたときに問題が発生しないか
      • それぞれが独立しているため機能上問題がないが、複数アイテムの同時発射が可能なためUIが被るなどの可能性がある
  • ザコ敵にダメージを与えられるか、ボスにダメージを与えられるか
    • ザコとボスでダメージ計算の処理が別ファイルにわかれている
  • フレームレートが60FPSを下回らないか
    • 爆弾をばらまきまくるので処理落ちしないか心配
  • 敵がアイテムとして新規アイテム(ItemCluster)を落とすようになっているか
    • アイテムの設定ミスがあると落とさない
  • 画面の下で発射した時どうなるか
    • ClusterBombは0.5秒は落下する
    • 子弾は画面下限+0.5mの位置で爆発する
    • ならば画面下部でClusterBombを発射した場合の挙動はどうなるのか?
  • ゲームバランス
    • ボスにぶつけたときにHPを著しく削りすぎないか
  • アイテム取得後、取得済みアイテムとしてセーブ/ロードに反映できるか

以上です。
ゲーム特有のルールや、ゲームバランスの考慮部分はAIからは出てこないのでは……と、この時は思っていました。

cursor claude-4-sonnetが出したテスト観点

以下の様に質問を行いました。

チャット欄にずらりとテスト観点が並んで見づらかったのでmarkdownのファイルで出力してもらいました。Agentだとファイル作成も行ってくれるので楽です。

壮大なテスト計画を出してくれて、この記事に載せるのは大変だったのでGithubに載せました。
かいつまんで見ていきます。
https://github.com/jam0824/AIWritesTestPlan/blob/main/ClusterBattery_TestPlan.md

結果

仕込んだバグを拾えているか

先述した忘れ物を拾えるようなテスト観点を出せているか確認します。

  • 他アイテムはすべてItemDataからダメージを呼んでいるのにこの実装では呼んでいない
    • インテグレーションテストで拾いたい
  • 親(ClusterBomb)のTag設定忘れで親は敵にダメージを与えられない。
    • システムテストで拾いたい

(拾えた)ItemDataからダメージを呼んでいない

見事に該当のバグを拾える観点を出していました。

2.2 弾丸システム統合
2.2.1 ConfigPlayerBullet連携テスト
TC-I006: 弾丸設定読み込みテスト

設定読み込みのテスト観点があるため、このテストを詳細化(人かAIかはさておき)するとおのずとItemDataからのダメージ呼び出しはテストが通ります。

(拾えた)Tag忘れで親は敵にダメージを与えられない

以下でテスト観点が挙げられています。

2.3 ゲームオブジェクト相互作用
2.3.1 敵システム連携テスト
TC-I009: 敵ダメージ適用テスト

敵にダメージが与えられるかは基本的な確認なので通って当たり前と人は考えますが、AIでも当然通すようです。
インテグレーションテストレベルでのテスト実施になっています。

***

このことから少なくとも私が気づいていた「忘れ物」は拾える状態です。
他に私が気づいていない「忘れ物」があっても、AIが出したテストで拾える可能性が高いです。

人が考えたテスト観点と比べてどうなのか?

では、先ほど私が挙げたテスト観点が含まれているか検証します。

  • (少し発想が必要)仕様に書かれている動作すべて
    • レベルアップ時の弾数アップの仕様のテスト観点がない
      • TC-U010: 子弾丸生成パラメータテストで、レベルアップ自体は確認できないがレベルに応じた生成弾数が正しいかは確認可能。レベルアップのテストはない
    • 他の計算や分離タイミングはユニットテストに含まれている
  • (足りない)ゲーム内特有のルールが適用されているか
    • (ない)プレイヤーが右向きから左向きになったときに発射方向が180度変わるか
    • (少し発想が必要)左向きに発射したときに画面端まで行ったら子弾発射を止め破棄されるか
      • ライフタイム管理の観点があるが、時間での削除の話に終始している
    • (少し発想が必要)プレイヤーキャラごとにダメージ倍率が変わるが反映されているか
      • 敵ダメージ適用テストで拾えそうだが、プレイヤーの観点追加が必要
    • (★ある)他の武器と合わせたときに問題が発生しないか
      • TC-I007: 他弾丸種別との競合回避テスト
  • (★ある)ザコ敵にダメージを与えられるか、ボスにダメージを与えられるか
    • TC-S001: 通常ステージ実用性テスト
    • TC-S002: ボス戦有効性テスト
  • (★ある)フレームレートが60FPSを下回らないか
    • TC-S006: 大量弾丸負荷テスト
  • (★ある)敵がアイテムとして新規アイテム(ItemCluster)を落とすようになっているか
    • TC-S009: ItemClusterBattery収集テスト
  • (ない)画面の下で発射した時どうなるか
  • (★ある)ゲームバランス
    • TC-S005: ゲーム難易度への影響評価テスト
  • (ない)アイテム取得後、取得済みアイテムとしてセーブ/ロードに反映できるか

「妄想」が多い

見ていて気付いたことが、ハルシネーションが多いことです。
シューティングゲームでは武器の切り替えが一般的ですが、このゲームには存在しません。
また弾幕系ゲームではよく使うテクニックであるオブジェクトプール(毎回オブジェクトの生成するのはコストがかかるので、破棄しないでためておいて再利用する)もありません。
つまりこのゲームにはないけれど、「シューティングではよくある処理」が並べられています。
「変更箇所から推定される記載されていないがテストすべきこと」というプロンプトが良くなかった可能性があります。

メモリリークやパフォーマンステストなど、抜けがちな観点が入っている

どうしても機能に注目してしまって、パフォーマンスの考慮が抜けることが私は多々あります。
そういったところがしっかり書かれているのは重要なポイントです。
今回実装した「クラスター爆弾」の場合は、大量のオブジェクトとエフェクトをばらまくので気を使う必要がある部分でした。

所感

人が考えたテスト観点からすると、確認しておくべきなのに足りないと思う部分もありました。
ただ仕込んだ「忘れ物」は2つとも拾えているし、人側が思い浮かばなかった観点も含まれます。
そう考えると
「AIに観点を出させて、人が観点を加える/取捨選択する」
という方法でテスト観点の補間や整理ができそうです。
「全部のテスト観点が出せないから使えない」といったゼロイチ思考ではなく、ある程度の手間の削減、ある程度の補間という効果を狙ってAIを使うと良さそうです。(そもそも「全部」ってなんだという話もある)

テストレベルとしては、どのテストレベルも妥当に見えています。
ただ物理演算の確認までするかというとそこまでは必要ないので、自分たちにとって必要なテストを選択したほうがいいです。
インテグレーションテストもオーダーしましたが、Unityだとオブジェクトの相互作用を見るならゲームを動かしてしまったほうが早いものも多いので、システムテストとほぼ同じにはなってしまうかもしれません。

おまけ

変更内容に絞ってテスト観点を出して

先ほどはハルシネーションが多かったので、以下のようなプロンプトで確認してみました。

結果は以下です。
https://github.com/jam0824/AIWritesTestPlan/blob/main/ClusterBattery_ImpactTest.md

GameManagerなどの変更したオブジェクトにフォーカスされているので、ちゃんとセーブやロードといったゲーム全体の影響範囲も含まれています。
ハルシネーションも相変わらずあります。手前の実験部分(そもそもハルシネーションがあった部分)も読み込んでいることが影響していそうです。

影響範囲もある程度出せているので、こちらのプロンプトの方が良さそうに見えています。
ただ「影響範囲:システム全体」といったように、それはそう……みたいなのも多いです。

o3 Max Modeを使ってみた

こちらは……
そもそもうまくいかなかったです。今回の変更ではなく、どこから持ってきたのか、かなり前の変更も含まれています。
仕様通りの確認に終始し、影響範囲への言及が特になさそうでした。

https://github.com/jam0824/AIWritesTestPlan/blob/main/ChangeScope_TestPerspectives.md

Discussion