テスト駆動開発

テスト駆動開発( TDD ) は、ソフトウェアが完全に開発される前にソフトウェア要件をテスト ケースに変換し、すべてのテスト ケースに対してソフトウェアを繰り返しテストすることですべてのソフトウェア開発を追跡するソフトウェア開発プロセスです。これは、ソフトウェアが最初に開発され、テスト ケースが後で作成されるのとは対照的です。

この技術を開発または「再発見」したとされるソフトウェア エンジニアのケント ベック[1]は 2003 年に、TDD はシンプルな設計を奨励し、自信を与えると述べました。[2]

テスト駆動開発は、1999 年に始まったエクストリーム プログラミング[3]のテストファースト プログラミング概念に関連していますが、最近ではそれ自体がより一般的な関心を集めています。[4]

プログラマーは、この概念を古い技術で開発されたレガシー コードの改善とデバッグ にも適用します。[5]

テスト駆動の開発サイクル

テスト駆動開発ライフサイクルのグラフィック表現

次のシーケンスは、書籍『Test-Driven Development by Example』に基づいています: [2]

1. テストを追加する
新しい機能の追加は、機能の仕様が満たされていれ合格するテストを作成することから始まります。開発者は、使用例ユーザー ストーリーについて質問することで、これらの仕様を知ることができます。テスト駆動開発の主な利点は、開発者がコードを記述する前に要件に集中できることですこれは、単体テストがコードの後に​​のみ記述される通常のやり方とは対照的です。
2. すべてのテストを実行します。新しいテストは予想される理由で失敗するはずです
これは、目的の機能には実際に新しいコードが必要であることを示しています。テスト ハーネスが正しく機能していることを検証しますこれにより、新しいテストに欠陥があり、常に合格する可能性が排除されます。
3. 新しいテストに合格する最も単純なコードを作成します。
テストに合格する限り、洗練されていないコードやハードコードも許容されます。いずれにせよ、コードはステップ 5 で磨き上げられます。テストされた機能以外のコードは追加しないでください。
4. すべてのテストに合格するはずです
いずれかが不合格の場合は、合格するまで新しいコードを修正する必要があります。これにより、新しいコードがテスト要件を満たし、既存の機能が損なわれないことが保証されます。
5. 必要に応じてリファクタリングを行い、各リファクタリング後にテストを使用して機能が維持されていることを確認します。
コードは読みやすさと保守性を考慮してリファクタリングされています。特に、ハードコーディングされたテスト データは削除する必要があります。各リファクタリングの後にテスト スイートを実行すると、既存の機能が壊れていないことを確認できます。
繰り返す
上記のサイクルは、新しい機能ごとに繰り返されます。テストは小規模かつ段階的に行う必要があり、コミットは頻繁に行う必要があります。そうすれば、新しいコードがいくつかのテストに失敗した場合でも、プログラマは過剰なデバッグを行わずに、単純に元に戻すか元に戻すことができます外部ライブラリを使用する場合、ライブラリにバグがあるか、すべての機能に対応できるほど機能が豊富ではないと思われる何らかの理由がない限り、ライブラリ自体を効果的にテストするだけの小さなテストを作成しないことが重要です。開発中のソフトウェアのニーズ。

開発スタイル

テスト駆動開発の使用にはさまざまな側面があります。たとえば、「馬鹿げてもシンプルにしなさい」( KISS ) や「それは必要ない」 (YAGNI)という原則があります。テストに合格するために必要なコードのみを記述することに重点を置くことで、多くの場合、他の方法で実現するよりも設計がよりクリーンで明確になります。[2] Kent Beck は、 「Test-Driven Development by Example」の中で、「成功するまで偽りなさい」という原則も提案しています

設計パターンなどの高度な設計概念を実現するには、その設計を生成するテストが作成されます。コードはターゲット パターンより単純なままですが、必要なテストはすべて合格します。最初は不安になるかもしれませんが、開発者は重要なことだけに集中できるようになります。

最初にテストを作成する: テストは、テストする機能の前に作成する必要があります。これには多くの利点があると主張されています。開発者はアプリケーションを後で追加するのではなく、最初からアプリケーションをテストする方法を考慮する必要があるため、アプリケーションがテストしやすいように作成されていることを確認するのに役立ちます。また、すべての機能のテストが確実に作成されるようにします。さらに、最初にテストを作成することで、製品要件をより深く、より早く理解することができ、テスト コードの有効性が確保され、ソフトウェアの品質に継続的に重点を置くことができます。[6]機能優先のコードを作成する場合、開発者や組織は開発者を次の機能に進めさせ、テストを完全に無視する傾向があります。最初の TDD テストは、必要なクラスとメソッドがまだ存在していない可能性があるため、最初はコンパイルさえできない可能性があります。それにもかかわらず、その最初のテストは、実行可能な仕様の始まりとして機能します。[7]

各テスト ケースは最初は失敗します。これにより、テストが実際に機能し、エラーをキャッチできることが保証されます。これが表示されると、基礎となる機能を実装できます。これは、「赤/緑/リファクタリング」という「テスト駆動開発のマントラ」につながりました。赤は失敗を意味し、緑は合格を意味します。テスト駆動開発では、失敗したテスト ケースを追加し、合格し、リファクタリングするという手順を常に繰り返します。各段階で期待されるテスト結果を受け取ると、開発者のコ​​ードのメンタル モデルが強化され、自信が高まり、生産性が向上します。

ユニットを小さく保つ

TDD の場合、ユニットはクラスとして定義されるか、モジュールと呼ばれることが多い関連関数のグループとして定義されるのが最も一般的です。ユニットを比較的小さく保つと、次のような重要な利点が得られると主張されています。

  • デバッグ作業の軽減 – テストの失敗が検出された場合、ユニットが小さいとエラーの追跡に役立ちます。
  • 自己文書化テスト – 小さなテスト ケースは読みやすく、理解しやすいです。[6]

テスト駆動開発の高度なプラクティスは、顧客が指定した基準が受け入れテストに自動化され、従来の単体テスト駆動開発 (UTDD) プロセスを推進する、受け入れテスト駆動開発 (ATDD) および仕様例につながる可能性があります[8]このプロセスにより、ソフトウェアが要件を満たしているかどうかを判断するための自動メカニズムが顧客に確保されます。ATDD を使用することで、開発チームは受け入れテストという満たすべき具体的な目標を設定できるようになり、各ユーザー ストーリーから顧客が本当に望んでいることに継続的に焦点を当てることができます。

ベストプラクティス

テスト構造

テスト ケースを効果的にレイアウトすると、必要なアクションがすべて確実に完了し、テスト ケースの読みやすさが向上し、実行の流れがスムーズになります。一貫した構造は、自己文書化されたテスト ケースを構築するのに役立ちます。テスト ケースに一般的に適用される構造には、(1) セットアップ、(2) 実行、(3) 検証、および (4) クリーンアップがあります。

  • セットアップ: テスト対象ユニット (UUT) またはテスト システム全体をテストの実行に必要な状態にします。
  • 実行: UUT をトリガー/駆動してターゲット動作を実行し、戻り値や出力パラメーターなどのすべての出力をキャプチャします。通常、この手順は非常に簡単です。
  • 検証: テストの結果が正しいことを確認します。これらの結果には、UUT の実行中または状態変化中にキャプチャされた明示的な出力が含まれる場合があります。
  • クリーンアップ: UUT またはテスト システム全体をテスト前の状態に復元します。この復元により、このテストの直後に別のテストを実行できるようになります。場合によっては、考えられるテスト失敗の分析に備えて情報を保存するために、クリーンアップはテストのセットアップ実行の直前にテストを開始する必要があります。[6]

個別のベストプラクティス

個人が従うことができるベスト プラクティスとしては、共通のセットアップ ロジックと破棄ロジックを適切なテスト ケースで利用されるテスト サポート サービスに分離し、各テスト オラクルがテストの検証に必要な結果のみに焦点を当て続けるようにすることです。非リアルタイム オペレーティング システムでの実行を許容できるように、時間関連のテストを設計します。遅延実行に対して 5 ~ 10% のマージンを許容するという一般的な方法により、テスト実行における潜在的な偽陰性の数が減少します。また、テスト コードを製品コードと同じように扱うことも推奨されます。テストコードは、肯定的な場合と否定的な場合の両方で正しく動作し、長期間持続し、読みやすく保守しやすいものでなければなりません。チームが集まってテストやテストの実践をレビューし、効果的なテクニックを共有し、悪い習慣を見つけることができます。[9]

避けるべきプラクティス、または「アンチパターン」

  • テスト ケースの有無は、以前に実行されたテスト ケースから操作されたシステム状態に依存します (つまり、単体テストは常に既知の事前構成された状態から開始する必要があります)。
  • テスト ケース間の依存関係。テスト ケースが相互に依存しているテスト スイートは脆弱で複雑です。実行順序は推測されるべきではありません。初期のテスト ケースまたは UUT の構造の基本的なリファクタリングは、関連するテストにますます広範囲に影響を与えるスパイラルを引き起こします。
  • 相互依存テスト。相互依存したテストにより、連鎖的な偽陰性が発生する可能性があります。UUT に実際の障害が存在しない場合でも、初期のテスト ケースで障害が発生すると、後のテスト ケースが中断され、欠陥分析とデバッグの労力が増加します。
  • 正確な実行、動作、タイミング、またはパフォーマンスをテストします。
  • 「全知の神託」を構築する。必要以上に検査を行うオラクルは高価であり、時間の経過とともに脆弱になります。この非常に一般的なエラーは、複雑なプロジェクト全体で微妙ではありますが広範な時間の減少を引き起こすため、危険です。[9] [説明が必要]
  • テスト実装の詳細。
  • テストの実行が遅い。

利点

2005 年の調査では、TDD を使用するとより多くのテストを作成することを意味し、その結果、より多くのテストを作成したプログラマーの生産性が向上する傾向があることがわかりました。[10]コードの品質、および TDD と生産性の間のより直接的な相関関係に関する仮説は決定的ではありませんでした。[11]

新しい (「グリーンフィールド」) プロジェクトで純粋な TDD を使用しているプログラマは、デバッガを呼び出す必要性をほとんど感じないと報告しましたバージョン管理システムと組み合わせて使用​​すると、テストが予期せず失敗した場合、すべてのテストに合格した最後のバージョンにコードを戻す方が、デバッグよりも生産性が高くなる場合があります。[12]

テスト駆動開発では、正確性を単純に検証するだけでなく、プログラムの設計を推進することもできます。[13]最初にテスト ケースに焦点を当てることで、機能がクライアント (最初のケースではテスト ケース) によってどのように使用されるかを想像する必要があります。したがって、プログラマは実装の前にインターフェイスに関心を持ちます。この利点は、数学的な主張や先入観を通じてではなく、テスト ケースを通じてコードにアプローチするため、 契約による設計を補完するものです。

テスト駆動開発では、必要に応じて小さなステップを実行できます。最初の目標はテストに合格することなので、プログラマは目の前のタスクに集中できます。例外的なケースとエラー処理は最初は考慮されておらず、これらの無関係な状況を作成するためのテストは個別に実装されます。テスト駆動開発では、このようにして、記述されたすべてのコードが少なくとも 1 つのテストでカバーされることが保証されます。これにより、プログラミング チームと後続のユーザーは、コードに対するより高いレベルの信頼を得ることができます。

TDD を使用すると単体テスト コードがあるため、TDD を使用しない場合よりも多くのコードが必要になるのは事実ですが、Müller と Padberg のモデルに基づくと、合計のコード実装時間は短くなる可能性があります。[14]多数のテストは、コード内の欠陥の数を制限するのに役立ちます。テストを早期かつ頻繁に行うことで、開発サイクルの早い段階で欠陥を発見し、欠陥が蔓延して費用がかかる問題になるのを防ぐことができます。通常、プロセスの早い段階で欠陥を排除すると、プロジェクト後半での長くて退屈なデバッグが回避されます。

TDD により、コードがよりモジュール化され、柔軟で拡張可能になります。この効果は、開発者が独立して作成およびテストでき、後で統合できる小さな単位の観点からソフトウェアを考えることがこの方法論で求められるため、多くの場合発生します。これにより、より小さく、より焦点を絞ったクラス、より緩やかな結合、よりクリーンなインターフェイスが実現します。モック オブジェクト設計パターンの使用は、コード全体のモジュール化にも貢献します。このパターンでは、単体テスト用のモック バージョンと展開用の「実際の」バージョンの間でモジュールを簡単に切り替えることができるようにコードを記述する必要があるためです。

不合格のテスト ケースに合格するために必要以上のコードは記述されないため、自動テストはすべてのコード パスをカバーする傾向があります。たとえば、TDD 開発者がelse既存のifステートメントにブランチを追加するには、開発者はまずブランチを動機付ける失敗するテスト ケースを作成する必要があります。その結果、TDD による自動テストは非常に徹底的になる傾向があり、コードの動作における予期せぬ変化が検出されます。これにより、開発サイクルの後半での変更によって他の機能が予期せず変更される場合に発生する可能性のある問題が検出されます。

Madeyski [15]は、オブジェクト間の結合度の低さ (CBO) に関して、従来の Test-Last アプローチや正当性テストのアプローチよりも TDD 実践の優位性について (200 名を超える開発者による一連の実験室実験を通じて) 経験的証拠を提供しました。 。平均効果サイズは、実行された実験のメタ分析に基づいて中程度の (ただし大に近い) 効果を表しており、これは実質的な発見です。これは、TDD プログラミングの実践により、より良いモジュール化 (つまり、よりモジュール化された設計)、開発されたソフトウェア製品の再利用とテストが容易になることを示唆しています。[15] Madeyski は、ブランチ カバレッジ (BC) と突然変異スコア インジケーター (MSI) を使用して、単体テストに対する TDD 実践の効果も測定しました[16] [17][18]これらは、それぞれ単体テストの徹底度と障害検出の有効性を示す指標です。ブランチ カバレッジに対する TDD の効果は中程度であり、実質的な効果であると考えられます。[15]これらの発見は、その後、TDD のさらに小規模な実験評価によって確認されました。[19] [20] [21] [22]

制限事項

テスト駆動開発では、単体テストが広範に使用されるため、成否を判断するために完全な機能テストが必要な状況では、十分なテストが実行されません。[23]これらの例には、ユーザー インターフェイス、データベースと連携するプログラム、および特定のネットワーク構成に依存するものがありますTDD では、開発者がそのようなモジュールに最小限のコードを組み込み、外部の世界を表すフェイクやモックを使用して、テスト可能なライブラリ コード内のロジックを最大化することを推奨しています。[24]

経営者のサポートは欠かせません。組織全体がテスト駆動開発によって製品が改善されると信じていないと、経営陣はテストの作成に費やした時間が無駄だと感じるかもしれません。[25]

テスト駆動開発環境で作成される単体テストは、通常、テスト対象のコードを作成する開発者によって作成されます。したがって、テストはコードと盲点を共有する可能性があります。たとえば、開発者が特定の入力パラメーターをチェックする必要があることに気づいていない場合、テストもコードもそれらのパラメーターを検証しない可能性が高くなります。別の例: 開発者が開発中のモジュールの要件を誤解した場合、開発者が作成するコードと単体テストは両方とも同じように間違ったものになります。したがって、テストは合格し、正しいという誤った感覚を与えます。

多数の単体テストに合格すると、誤ったセキュリティ意識がもたらされ、統合テストコンプライアンス テストなどの追加のソフトウェア テスト活動が少なくなる可能性があります

テストはプロジェクトのメンテナンスのオーバーヘッドの一部になります。不適切に作成されたテスト (ハードコーディングされたエラー文字列を含むテストなど) は、それ自体が失敗する傾向があり、維持にコストがかかります。これは、脆弱なテストの場合に特に当てはまります。[26]定期的に誤った障害を生成するテストが無視され、実際の障害が発生したときに検出できない可能性があります。たとえば、エラー文字列を再利用することで、メンテナンスが少なくて簡単なテストを作成することが可能です。これは、上記のコードリファクタリング フェーズ での目標とすべきです。

過剰な数のテストを作成して維持すると、時間がかかります。また、より柔軟なモジュール (テストが制限されている) では、テストを変更することなく新しい要件を受け入れることができます。これらの理由から、極端な条件のみ、またはデータの小さなサンプルのみをテストする方が、一連の非常に詳細なテストを行うよりも調整が簡単になる可能性があります。

繰り返される TDD サイクル中に達成されるカバレッジのレベルとテストの詳細は、後日簡単に再作成することはできません。したがって、これらのオリジナルのテスト、または初期のテストは、時間が経つにつれてますます貴重になります。早めに修正するのが戦略です。また、貧弱なアーキテクチャ、貧弱な設計、または貧弱なテスト戦略によって変更が遅れ、数十の既存のテストが失敗する場合には、それらを個別に修正することが重要です。単に削除、無効化、または軽率に変更すると、テスト カバレッジに検出できない穴が生じる可能性があります。

テスト主導の作業

テスト駆動開発は、ソフトウェア開発以外でも、製品チームとサービスチームの両方でテスト駆動作業として採用されています。[27]テストを成功させるには、ミクロレベルとマクロレベルで実施する必要があります。クラス内のすべてのメソッド、すべての入力データ値、ログ メッセージ、エラー コード、その他のデータ ポイントはテストする必要があります。[28] TDD と同様に、ソフトウェア以外のチームは、開始前に作業の各側面の品質管理(QC) チェック (通常は自動テストではなく手動テスト) を開発します。これらの QC チェックは、設計に情報を提供し、関連する結果を検証するために使用されます。TDD シーケンスの 6 つのステップは、セマンティックに若干の変更を加えて適用されます。

  1. 「チェックを追加」が「テストを追加」に置き換わります
  2. 「すべてのチェックを実行」が「すべてのテストを実行」に置き換わります。
  3. 「コードを書く」を「仕事をする」に置き換える
  4. 「すべてのチェックを実行」が「テストの実行」に置き換わります。
  5. 「作業のクリーンアップ」が「コードのリファクタリング」に代わる
  6. "繰り返す"

TDDとATDD

テスト駆動開発は受け入れテスト駆動開発(ATDD) に関連していますが、それとは異なります。[29] TDD は主に、一連の操作を正しく実行する、適切に記述されたコード単位 (関数、クラス、またはモジュール) の作成を支援する開発者用ツールです。ATDD は、要件が明確に定義されていることを確認するための、顧客、開発者、テスター間のコミュニケーション ツールです。TDD にはテストの自動化が必要です。ATDD はそうではありませんが、自動化は回帰テストに役立ちます。TDD で使用されるテストは、コード単位が要件の一部を実装するため、ATDD テストから派生することがよくあります。ATDD テストは、顧客が判読できる必要があります。TDD テストはその必要はありません。

TDD と BDD

BDD (動作駆動開発) は、TDD と ATDD のプラクティスを組み合わせたものです。[30] 最初にテストを書く練習も含まれていますが、実装単位をテストするテストではなく、動作を記述するテストに焦点を当てています。JBehave、 Cucumber、Mspec、Specflowなどのツールは、製品所有者、開発者、テスト エンジニアが一緒に動作を定義し、自動テストに変換できる構文を提供します。

コードの可視性

テスト スイートのコードは、テスト対象のコードにアクセスできる必要があることは明らかです。一方、情報の隠蔽、カプセル化、関心事の分離などの通常の設計基準は損なわれるべきではありません。したがって、TDD の単体テスト コードは、通常、テスト対象のコードと 同じプロジェクトまたはモジュール内に記述されます。

オブジェクト指向設計では、これは依然としてプライベート データおよびメソッドへのアクセスを提供しません。したがって、単体テストには追加の作業が必要になる場合があります。Javaおよびその他の言語では、開発者はリフレクションを使用してプライベート フィールドおよびメソッドにアクセスできます。[31]あるいは、内部クラスを使用して単体テストを保持し、それを囲んでいるクラスのメンバーと属性を可視にすることもできます。.NET Frameworkおよびその他のプログラミング言語では、部分クラスを使用して、テストがアクセスするプライベート メソッドとデータを公開することがあります。

このようなテスト ハックが運用コードに残らないようにすることが重要です。Cおよびその他の言語ではそのような追加クラスやその他すべてのテスト関連コードの周囲に などのコンパイラディレクティブを配置して、それらがリリースされたコードにコンパイルされるのを防ぐことができます。#if DEBUG ... #endifこれは、リリースされたコードが単体テストされたものとまったく同じではないことを意味します。最終リリース ビルドで、数は少ないがより包括的なエンドツーエンドの統合テストを定期的に実行することで、(とりわけ) テスト ハーネスの側面に微妙に依存する運用コードが存在しないことを確認できます。

TDD の実践者の間では、とにかくプライベートなメソッドとデータをテストすることが賢明であるかどうかについて、ブログやその他の著作で文書化されている議論があります。プライベート メンバーは変更される可能性がある単なる実装の詳細であり、テストの数を損なうことなくそうすることを許可されるべきだと主張する人もいます。したがって、パブリック インターフェイスまたはサブクラス インターフェイス (一部の言語では「保護された」インターフェイスと呼ばれます) を通じてクラスをテストすれば十分です。[32]また、機能の重要な側面はプライベート メソッドで実装でき、それらを直接テストすることで、より小規模で直接的な単体テストの利点が得られると言う人もいます。[33] [34]

TDD用ソフトウェア

TDD には便利なテスト フレームワークやツールが多数あります。

xUnit フレームワーク

開発者は、一般にxUnit (1998 年に作成された SUnit から派生したもの) と総称されるコンピューター支援テスト フレームワークを使用して、テスト ケースを作成し、自動的に実行できます。xUnit フレームワークは、アサーション スタイルのテスト検証機能と結果レポートを提供します。これらの機能は、実行検証の負担を独立した後処理アクティビティからテスト実行に含まれるアクティビティに移すため、自動化にとって重要です。これらのテスト フレームワークによって提供される実行フレームワークにより、すべてのシステム テスト ケースまたはさまざまなサブセットを他の機能とともに自動的に実行できます。[35]

タップ結果

テスト フレームワークは、1987 年に作成された言語に依存しないTest Anything Protocol の単体テスト出力を受け入れることができます

フェイク、モック、統合テスト

単体テストは、それぞれが1 つのコード単位をテストするため、そのように名付けられています。複雑なモジュールには 1,000 個の単体テストが含まれる場合がありますが、単純なモジュールには 10 個しかない場合があります。TDD に使用される単体テストは、ネットワーク接続はもちろん、プログラム内のプロセス境界を越えてはなりません。そうすると遅延が発生し、テストの実行が遅くなり、開発者がスイート全体を実行する意欲を失います。外部モジュールまたはデータへの依存関係を導入すると、単体テストが統合テストに変わります相互に関連するモジュールのチェーン内で 1 つのモジュールが誤動作した場合、障害の原因をどこに求めるべきかはすぐにはわかりません。

開発中のコードがデータベース、Web サービス、またはその他の外部プロセスやサービスに依存している場合、単体テスト可能な分離を強制することは、よりモジュール化され、よりテストしやすく、より再利用可能なコードを設計する機会と推進力にもなります。[36] 2 つの手順が必要です。

  1. 最終設計で外部アクセスが必要な場合は、利用可能なアクセスを記述するインターフェイスを定義する必要があります。TDD に関係なくこれを行う利点については、依存関係逆転の原則を参照してください。
  2. インターフェイスは 2 つの方法で実装する必要があります。1 つは実際に外部プロセスにアクセスし、もう 1 つは偽またはモックです。偽のオブジェクトは、「人物オブジェクトが保存されました」などのメッセージをトレース ログに追加するだけでよく、これに対してテストアサーションを実行して正しい動作を検証できます。モック オブジェクトは、たとえば、人の名前やその他のデータが期待どおりでない場合など、テストを失敗させる可能性のあるテスト アサーションをそれ自体に含んでいるという点で異なります。

表面上はデータ ストアまたはユーザーからデータを返す偽オブジェクト メソッドとモック オブジェクト メソッドは、テストが信頼できる同じ現実的なデータを常に返すため、テスト プロセスに役立ちます。また、エラー処理ルーチンを開発して確実にテストできるように、事前定義された障害モードに設定することもできます。フォールト モードでは、メソッドが無効、不完全、またはnull の応答を返したり、例外をスローしたりすることがあります。データ ストア以外の偽のサービスも TDD で役立つ場合があります。偽の暗号化サービスは、実際には渡されるデータを暗号化しない可能性があります。偽の乱数サービスは常に 1 を返す可能性があります。偽の実装またはモック実装は、依存関係注入の例です。

テスト ダブルは、UUT が依存するシステム機能 (通常はクラスまたは関数) を置き換えるテスト固有の機能です。テスト ダブルをシステムに導入できるタイミングは、リンクと実行の 2 つです。リンク時置換は、テスト ダブルがロード モジュールにコンパイルされるときに行われ、テストを検証するために実行されます。このアプローチは通常、コンパイルにハードウェア レベルのコードを倍増する必要があるターゲット環境以外の環境で実行する場合に使用されます。リンカー置換の代替手段は、テスト ケースの実行中に実際の機能が置換される実行時置換です。この置換は通常、既知の関数ポインタの再割り当てまたはオブジェクトの置換を通じて行われます。

テスト ダブルにはさまざまなタイプがあり、複雑さも異なります。

  • ダミー– ダミーはテスト ダブルの最も単純な形式です。必要に応じてデフォルトの戻り値を提供することで、リンカー時の置換を容易にします。
  • スタブ– スタブは単純なロジックをダミーに追加し、さまざまな出力を提供します。
  • スパイ – スパイはパラメーターと状態情報をキャプチャして利用可能にし、プライベート情報のテスト コードにアクセサーを公開することで、より高度な状態検証を可能にします。
  • モック– モックは、テスト固有の動作を検証し、パラメーター値と呼び出し順序を確認するために、個々のテスト ケースによって指定されます。
  • シミュレータ – シミュレータは、ターゲット能力 (2 倍になるもの) のより忠実度の高い近似を提供する包括的なコンポーネントです。通常、シミュレータには多大な追加の開発作業が必要です。[6]

このような依存関係の注入の結果として、実際のデータベースやその他の外部アクセス コードが TDD プロセス自体によってテストされることはありません。これによって発生する可能性のあるエラーを回避するには、上で説明したインターフェイスの「実際の」実装を使用してテスト駆動コードをインスタンス化する他のテストが必要です。これらは統合テストであり、TDD 単体テストとはまったく別のものです。テストの数は少なく、単体テストよりも実行頻度が低くなります。それでも、同じテスト フレームワークを使用して実装できます。

永続ストアまたはデータベースを変更する統合テストは、テストが失敗した場合でも、ファイルまたはデータベースの初期状態と最終状態を考慮して常に慎重に設計する必要があります。これは多くの場合、次の手法をいくつか組み合わせて使用​​して実現されます。

  • このTearDownメソッドは、多くのテスト フレームワークに不可欠です。
  • try...catch...finally 利用可能な場合は例外処理構造。
  • トランザクションには、おそらく書き込み、読み取り、および対応する削除操作がアトミックに含まれるデータベース トランザクション。
  • テストを実行する前にデータベースの「スナップショット」を取得し、各テストの実行後にスナップショットにロールバックします。これは、 AntNAntなどのフレームワーク、またはCruiseControlなどの継続的統合システムを使用して自動化できます。
  • テスト後にクリーンアップするのではなく、テスト前にデータベースをクリーンな状態に初期化しますこれは、詳細な診断を実行する前にデータベースの最終状態を削除することで、クリーンアップによってテストの失敗の診断が困難になる可能性がある場合に関連する可能性があります。

複雑なシステム向けの TDD

大規模で困難なシステムで TDD を実行するには、モジュラー アーキテクチャ、公開されたインターフェイスを備えた明確に定義されたコンポーネント、プラットフォームの独立性を最大限に高める規律あるシステム レイヤ化が必要です。これらの実証済みのプラクティスにより、テスト容易性が向上し、ビルドとテストの自動化の適用が容易になります。[6]

テスト容易性を考慮した設計

複雑なシステムには、さまざまな要件を満たすアーキテクチャが必要です。これらの要件の主要なサブセットには、システムの完全かつ効果的なテストのサポートが含まれます。効果的なモジュール設計により、効果的な TDD に不可欠な特性を共有するコンポーネントが得られます。

  • 高い凝集性により、各ユニットが一連の関連機能を提供し、それらの機能のテストの保守が容易になります。
  • 低結合により、各ユニットを個別に効果的にテストできます。
  • 公開されたインターフェイスは、コンポーネントのアクセスを制限し、テストの連絡先として機能するため、テストの作成が容易になり、テストと実稼働ユニットの構成間の最高の忠実度が確保されます。

効果的なモジュラー アーキテクチャを構築するための主要な手法は、単一のシステム レベルの実行シナリオに焦点を当てた一連のシーケンス チャートを構築するシナリオ モデリングです。シナリオ モデルは、特定の刺激に応じたコンポーネント間の相互作用の戦略を作成するための優れた手段を提供します。これらの各シナリオ モデルは、コンポーネントが提供する必要があるサービスまたは機能の豊富な要件セットとして機能し、これらのコンポーネントとサービスが相互に対話する順序も決定します。シナリオ モデリングにより、複雑なシステムの TDD テストの構築が大幅に容易になります。[6]

大規模なチームのテストの管理

大規模なシステムでは、コンポーネントの品質の低下による影響は、相互作用の複雑さによってさらに大きくなります。この拡大により、大規模プロジェクトのコンテキストでは TDD のメリットがさらに早く得られます。ただし、テストの母集団全体の複雑さ自体が問題となり、潜在的な利益が損なわれる可能性があります。簡単なことのように聞こえますが、重要な最初のステップは、テスト コードも重要なソフトウェアであり、製品コードと同じ厳密さで作成および保守する必要があることを認識することです。

複雑なシステム内でテスト ソフトウェアのアーキテクチャを作成および管理することは、コア製品のアーキテクチャと同じくらい重要です。テスト ドライバーは、UUT、テスト ダブル、および単体テスト フレームワークと対話します。[6]

会議

最初の TDD カンファレンスは 2021 年 7 月に開催されました。[37]カンファレンスはYouTubeで録画されました[38]

こちらも参照

参考文献

  1. ^ ケント・ベック (2012 年 5 月 11 日)。「なぜケント・ベックはテスト駆動開発の「再発見」に言及しているのでしょうか?」2014 年12 月 1 日に取得
  2. ^ abc ケント州ベック (2002-11-08)。例によるテスト駆動開発ヴァシーム:アディソン・ウェスリー。ISBN 978-0-321-14653-3
  3. ^ リー・コープランド (2001 年 12 月)。「エクストリームプログラミング」。コンピューターワールド。2011 年 6 月 5 日のオリジナルからアーカイブ2011 年1 月 11 日に取得
  4. ^ ab ニューカーク、JW およびボロンツォフ、AA。『Microsoft .NET でのテスト駆動開発』、Microsoft Press、2004 年。
  5. ^ フェザーズ、M. レガシー コードを効果的に使用する、プレンティス ホール、2004 年
  6. ^ abcdefg 「複雑な組み込みシステム向けの効果的な TDD ホワイトペーパー」(PDF)パスファインダーソリューション。2016 年 3 月 16 日のオリジナル(PDF)からアーカイブされました。
  7. ^ “アジャイルなテスト駆動開発”. 機敏なシェルパ。2010年8月3日。2012 年 7 月 23 日にオリジナルからアーカイブされました2012 年 8 月 14 日に取得
  8. ^ Koskela, L.「テスト駆動: Java 開発者のための TDD と受け入れ TDD」、Manning Publications、2007
  9. ^ ab Pathfinder Solutions によるYouTubeの複雑なシステム向けのテスト駆動開発 (TDD) の紹介
  10. ^ エルドグムス、ハカン; モリシオ、トルキアーノ。「プログラミングに対するテストファーストアプローチの有効性について」。ソフトウェアエンジニアリングに関する IEEE トランザクション議事録、31(1)。2005 年 1 月。(NRC 47445)。2014 年 12 月 22 日にオリジナルからアーカイブされました2008 年 1 月 14 日に取得平均して、テスト優先の生徒はより多くのテストを作成し、その結果、より多くのテストを作成した生徒は生産性が高くなる傾向があることがわかりました。
  11. ^ プロフィット、ジェイコブ。「TDD は効果的であることが証明されました! それともそうですか?」。2008 年 2 月 6 日にオリジナルからアーカイブされました2008 年 2 月 21 日に取得したがって、TDD と品質の関係は、よく言っても問題があります。生産性との関係はさらに興味深いものです。生産性の数値が私にとってあまり良くないので、追跡調査があることを願っています。生産性とテスト数の間には否定できない相関関係がありますが、その相関関係は実際には非 TDD グループの方が強いです ( TDD グループの約半分が 95% の範囲外であるのに対し、非 TDD グループでは外れ値が1 つありました)。
  12. ^ ノエル、ロピス (2005 年 2 月 20 日)。「Stepping Through the Looking Glass: テスト駆動のゲーム開発 (パート 1)」。内からのゲーム2007 年 11 月 1 日に取得[TDD] を非テスト駆動開発アプローチと比較すると、すべての精神的なチェックとデバッガーのステップ実行を、プログラムが意図したとおりに正確に実行することを検証するコードに置き換えることになります。
  13. ^ ヘルヴィッヒ・マイヤー (2005). Projektgruppen の Projekt Engineering Ingenieurmässige Softwareentwicklung (2.、neu bearb. Aufl. ed.)。ミュンヘン: ファッハブッフフェルル。ライプツィヒはカール・ハンザー・フェルルです。p. 239.ISBN _ 978-3446400702
  14. ^ ミュラー、マティアス M.; パドバーグ、フランク。「テスト駆動開発の投資収益率について」(PDF)カールスルーエ大学、ドイツ: 6. S2CID 13905442。2017年 11 月 8 日 のオリジナル(PDF)からアーカイブ2012 年 6 月 14 日に取得 {{cite journal}}:引用ジャーナルが必要です|journal=(ヘルプ)
  15. ^ abc Madeyski、L.「テスト駆動開発 - アジャイル実践の経験的評価」、Springer、2010 年、ISBN 978-3-642-04287-4、1-245ページ。DOI: 978-3-642-04288-1 
  16. ^ 単体テストのブランチ カバレッジと突然変異スコア指標に対するテストファースト プログラミングの影響: 実験。L. Madeyski 著、情報およびソフトウェア技術 52(2): 169-184 (2010)
  17. ^ 単体テストの徹底的さと欠陥発見の有効性に対するペア プログラミングの効果について、L. Madeyski PROFES 2007: 207-221
  18. ^ 単体テスト スイートの完全性と障害検出の有効性に対するペア プログラミングの影響。L. Madeyski 著ソフトウェア プロセス: 改善と実践 13(3): 281-295 (2008)
  19. ^ M. Pančur および M. Ciglarič、「生産性、コード、テストに対するテスト駆動開発の影響: 管理された実験」、情報とソフトウェア テクノロジー、2011 年、vol. 53、いいえ。6、557–573ページ、DOI: 10.1016/j.infsof.2011.02.002
  20. ^ D. Fucci、H. Erdogmus、B. Turhan、M. Oivo、および N. Juristo、「テスト駆動開発プロセスの分析: 最初にテストするか、最後にテストするかは本当に重要ですか?」、IEEEソフトウェアエンジニアリングに関するトランザクション、2017、vol. 43、いいえ。7、597–614ページ、DOI: 10.1109/TSE.2016.2616877
  21. ^ A. Tosun、O. Dieste Tubio、D. Fucci、S. Vegas、B. Turhan、H. Erdogmus、A. Santos、M. Oivo、K. Toro、J. Jarvinen、および N. Juristo、「業界外部品質と生産性に対するテスト駆動開発の影響に関する実験」、Empirical Software Engineering、2016 年、vol. 22、1–43 ページ、DOI: 10.1007/s10664-016-9490-0
  22. ^ B. Papis、K. Grochowski、K. Subzda、K. Sijko、「実際の産業プロジェクトに取り組むインターンによるテスト駆動開発の実験的評価」、IEEE Transactions on Software Engineering、2020 年、DOI: 10.1109/TSE.2020.3027522
  23. ^ 「TDD の問題」. ダルケス科学.com。2009-12-29 2014 年 3 月 25 日に取得
  24. ^ ハンター、アンドリュー (2012-10-19)。「単体テストは使いすぎていませんか?」シンプルトーク.com 2014 年 3 月 25 日に取得
  25. ^ スティーブ、ローラン (2006 年 11 月 6 日)。「テスト」(PDF)HP 研究所2009 年 8 月 12 日に取得
  26. ^ 「脆弱なテスト」.
  27. ^ Leybourn, E. (2013)アジャイル組織の指揮: ビジネス管理への無駄のないアプローチロンドン: IT ガバナンス出版: 176-179。
  28. ^ モハン、ガヤスリ。「フルスタックテスト」。www.thoughtworks.com 2022-09-07に取得
  29. ^ リーンアジャイル受け入れテスト駆動開発: コラボレーションによるより良いソフトウェアボストン: アディソン・ウェスリー・プロフェッショナル。2011.ISBN _ 978-0321714084
  30. ^ "BDD" . 2015 年 4 月 28 日に取得
  31. ^ バートン、ロス (2003-11-12)。「単体テストのための Java アクセス保護の破壊」。オライリーメディア株式会社 2009 年 8 月 12 日に取得
  32. ^ ヴァン・ロッサム、グイド; ワルシャワ、バリー (2001 年 7 月 5 日)。「PEP 8 -- Python コードのスタイル ガイド」。Python ソフトウェア財団2012 年5 月 6 日に取得
  33. ^ ジェームズ、ニューカーク (2004 年 6 月 7 日)。「プライベート メソッド/メンバー変数のテスト - すべきか、すべきではないか」。マイクロソフト株式会社2009 年 8 月 12 日に取得
  34. ^ ストール、ティム (2005 年 3 月 1 日)。「.NET でプライベート メソッドと保護されたメソッドをテストする方法」。コードプロジェクト2009 年 8 月 12 日に取得
  35. ^ 「複雑な組み込みシステム向けの効果的な TDD ホワイトペーパー」. パスファインダーソリューション。2013 年 8 月 20 日にオリジナルからアーカイブされました2012 年 11 月 27 日に取得
  36. ^ ファウラー、マーティン (1999)。リファクタリング - 既存のコードの設計を改善します。ボストン: アディソン・ウェスリー・ロングマン社、ISBN 0-201-48567-2
  37. ^ ブナルジッチ、アレックス。「第 1 回国際テスト駆動開発 (TDD) カンファレンス」。TDD カンファレンス2021年7月20日閲覧
  38. ^ 第 1 回国際 TDD カンファレンス - 2021 年 7 月 10 日土曜日、オリジナルから 2021-12-21 にアーカイブ、 2021-07-20取得

外部リンク

  • WikiWikiWeb のテスト駆動開発
  • バートランド・マイヤー (2004 年 9 月)。「テストか仕様か? テストと仕様? 仕様からテスト!」。2005 年 2 月 9 日のオリジナルからアーカイブ。
  • TDD アプローチによる Microsoft Visual Studio チーム テスト
  • 時間と労力を節約する、保守可能な単体テストを作成する
  • テスト駆動開発 (TDD) を使用したアプリケーションの品質の向上
  • テスト駆動開発カンファレンス