← Back to Blog

単体テスト:意図しない変更に対する安全装置

単体テスト:意図しない変更に対する安全装置

単体テストには、変更したロジックの正しさを検証し、アプリケーションの動作に意図しない変更が生じていないかを見つける助けとなることが期待されます。AIで開発する場合は、タスクの範囲外の機能が損なわれていないことも確認する必要があります。

したがって、新機能が動くことの確認は、作業の一部にすぎません。変更が既存のアプリケーションのルールを壊していないかも確認する必要があります。

単体テストが守るべきもの

単体テストは、コードの個々の部分の動作を検証します。また、リグレッション(それまで動いていた動作を変更することで混入するバグ)に対する安全装置としても機能します。

この保護は、AIが生成したコードにとって特に価値があります。AIは特定の要件を満たしながら、そのまま維持されるべきロジックまで変更してしまうことがあるからです。

よく設計されたテストは、変更の境界を定める助けになります。

これが変更したい動作です。これらのルールは引き続き成り立たなければなりません。

リスクと実際の依存関係に応じてテストする

すべてのゲッター、セッター、単純なデータクラスに対して、自動的に個別のテストを書く必要はありません。

実装をなぞるだけのテストは、テスト数を増やしても、価値はあまり増えません。さらに変更を加えるたびに、保守の手間も増えます。

実用的な指針は次のとおりです。

個々の変更については、変更された動作と、それに依存するアプリケーションの部分に焦点を当てます。テストの範囲は、起こりうる影響に基づいて決めます。

判断ロジックを持たない単純なメソッドは、他のテストがその動作を十分にカバーしているなら、個別の単体テストは不要かもしれません。

ただし、単純だからといってAIがミスをしないと考える理由にはなりません。たった1行のメソッドでも、注文の価格やユーザーのアクセス権限を左右することがあります。

テストを追加する前に、3つの質問をしてください。

  1. このタスクでどの動作が変わるのか?

変更が要件を満たしているかを検証します。

  1. どのルールと依存コンポーネントが損なわれてはならないのか?

変更がリグレッションを引き起こしうる箇所を探します。

  1. そのテストは実際のバグを検出できるのか?

実装をそのままコピーするのではなく、期待される動作を検証します。

テストはビジネスロジックと結びついている必要がある

最も価値のある単体テストは、アプリケーションの具体的なルールを守ります。

たとえば割引計算メソッドの場合、数値を返すことを確認するだけでは不十分です。次のルールが引き続き成り立っているかを知る必要があります。

  • 割引が許容された上限を超えない。

  • 最終価格がゼロを下回らない。

  • 割引の対象外の顧客は満額を支払う。

  • 計算が定められた丸めルールに従っている。

メソッドとビジネスルールとの結びつきこそが、テストに目的を与えます。

実装は変わるかもしれません。しかし、タスクの意図した範囲外にあるルールは維持されなければなりません。

AITMのアプローチ

AITMは、AIによる開発を、プロジェクトのコンテキスト、コントラクト、アプリケーションの各部分間の依存関係と結びつけます。

実装の前に、アーキテクトが、何を変更すべきか、関連する部分がどうつながっているか、どのルールを維持しなければならないかを特定します。テストステップは、この情報を利用します。

つまり重要な問いは、AIがテストを書けるかどうかにとどまりません。AIがそのテストが何を、なぜ守るべきかを知っているかどうかです。

DiscountService.calculateDiscount() を変更する場合を考えてみましょう。OrderSummary.render() も割引計算に依存していることを、コントラクトが dependsOnModules で記録していれば、テストステップには、注文サマリーへの影響も確認する理由が生まれます。

計算そのものは単体テストで確認できます。その計算が注文サマリーとどう連携するかの確認には、結合テストのほうが適しているかもしれません。

このように、テストは、個々の変更が持つ実際の依存関係とリスクに基づいて設計されます。

あるタスクには10個、別のタスクには2,000個のテスト

すべてのタスクが同じ範囲のテストを必要とするわけではありません。

一例として、32,467個のテストがあるプロジェクトでは、小さな変更なら関連する10個のテストの実行で済むかもしれません。共有されているビジネスロジックを変更する場合は、2,000個が必要になるかもしれません。

的を絞ったテストの目的は、変更された動作とその依存関係をカバーするテストを選ぶことです。これによりフィードバックサイクルを短縮し、タスクのリスクに検証を集中できます。

大規模な変更や新しいリリースの前には、より広範なリグレッション確認や全テストスイートの実行にも、引き続き意味があります。

実行するテストの数は、変更の影響を反映したものであるべきです。

テストが失敗したら、その理由を理解する必要がある

テストの失敗が、常に同じ問題を示すとは限りません。次のような状況を区別する必要があります。

  • 実装にバグが混入した。

  • 変更が意図せず既存のビジネスルールに違反した。

  • 新機能が意図的に期待される動作を変えたため、テストを更新する必要がある。

タスクの要件、コントラクト、ビジネスロジックの記述が、この判断のためのコンテキストを提供します。

要件が正当に変更されたのなら、対応するテストも変更しなければなりません。維持されるべきルールにAIが違反したのなら、実装を修正する必要があります。

テストが再び通るようにテストを書き換えるだけでは、本当のバグを覆い隠してしまうおそれがあります。

単体テストは保護の第一層

テストの各レベルは、それぞれ異なる役割を持ちます。

  • 単体テストは、ロジックの個々の部分を検証します。

  • 結合テストは、モジュールやシステムがどのように連携するかを検証します。

  • E2Eテストは、稼働中のアプリケーションでユーザーの一連の操作を最初から最後まで確認します。

これらの層は互いを補い合います。それぞれが異なる種類のバグを捕捉し、異なる視点から変更を検証する助けとなります。

そのためAIで開発する際には、2つのことを確認します。タスクが完了したか、そして意図した範囲外の動作が損なわれていないか、です。

タスクの要件、ビジネスロジック、依存関係、テストを結びつけることは、AITMのオーケストレーションの重要な一部です。

単体テスト:意図しない変更に対する安全装置 — スクリーンショット 1

例:単体テスト

詳しくは https://www.aitm.cz をご覧ください。

#AITM #UnitTesting #SoftwareTesting #AI #SoftwareDevelopment