テストは、コードが期待どおりに動作することを保証するのに役立ちますが、テストのビルドにかかる時間と労力は、機能開発などの他のタスクから時間がかかります。 このコストでは、テストから最大値を抽出することが重要です。 この記事では、単体テストの価値とシフト左のテスト戦略に焦点を当てて、DevOps テストの原則について説明します。
ほとんどのテストを作成するために専用のテスト担当者が使用され、多くの製品開発者は単体テストの記述を学習しませんでした。 テストの作成が難しすぎるか、作業が多すぎるように見える場合があります。 単体テスト戦略が機能するかどうか、記述の不十分な単体テストでの悪い経験、単体テストが機能テストに代わるという懸念に対して懐疑的な意見が生まれる可能性があります。
DevOps テスト戦略を実装するには、実用的であり、勢いを構築することに重点を置きます。 新しいコードや、クリーンにリファクタリングできる既存のコードの単体テストを強く求めることもできますが、レガシ コードベースで依存関係を許可することは理にかなっている可能性があります。 製品コードの重要な部分で SQL を使用する場合、ユニットテストでそのレイヤーをモックする代わりに、SQL リソース プロバイダーに依存できるようにすることは、短期的な進捗のアプローチとなる可能性があります。
DevOps 組織が成熟すると、リーダーがプロセスを改善しやすくなります。 変化には抵抗があるかもしれませんが、アジャイル組織は明確に配当を支払う変化を評価します。 機能開発を通じて新しい価値を生み出すために投資する時間が長くなるため、障害が少ない高速なテスト実行のビジョンを簡単に販売できる必要があります。
DevOps テスト分類
テスト分類の定義は、DevOps テスト プロセスの重要な側面です。 DevOps テスト分類では、個々のテストが依存関係と実行にかかる時間によって分類されます。 開発者は、さまざまなシナリオで使用する適切な種類のテストと、プロセスのさまざまな部分で必要なテストを理解する必要があります。 ほとんどの組織では、次の 4 つのレベルでテストが分類されます。
- L0 テストと L1 テストは 単体テスト、またはテスト対象のアセンブリ内のコードに依存するテストであり、それ以外のテストはありません。 L0 は、高速なメモリ内単体テストの幅広いクラスです。
- L2 は、アセンブリに加えて、SQL やファイル システムなどの他の依存関係を必要とする 機能テスト です。
- L3 機能テストは、テスト可能なサービスデプロイに対して実行されます。 このテスト カテゴリにはサービスのデプロイが必要ですが、主要なサービス の依存関係にスタブを 使用する場合があります。
- L4 テストは、運用環境に対して実行される 統合テスト の制限付きクラスです。 L4 テストには完全な製品展開が必要です。
すべてのテストを常に実行するのが理想的ですが、実現可能ではありません。 チームは、DevOps プロセス内の各テストを実行する場所を選択し、 シフト左 または シフト右の 戦略を使用して、プロセスの前または後で異なるテストの種類を移動できます。
たとえば、開発者はコミットする前に常に L2 テストを実行し、L3 テストの実行が失敗した場合はプル要求が自動的に失敗し、L4 テストが失敗した場合はデプロイがブロックされる可能性があります。 特定のルールは組織によって異なる場合がありますが、組織内のすべてのチームに対する期待を適用することで、全員が同じ品質ビジョンの目標に向かって移動します。
単体テストのガイドライン
L0 および L1 単体テストの厳密なガイドラインを設定します。 これらのテストは、非常に高速で信頼性の高いテストである必要があります。 たとえば、アセンブリ内の L0 テストあたりの平均実行時間は 60 ミリ秒未満にする必要があります。 アセンブリ内の L1 テストあたりの平均実行時間は 400 ミリ秒未満にする必要があります。 このレベルのテストは 2 秒を超えてはなりません。
1 つの Microsoft チームが、6 分未満で 60,000 を超える単体テストを並列実行します。 目標は、この時間を 1 分未満に減らすことです。 チームは、次のグラフのようなツールを使用して単体テストの実行時間を追跡し、許可された時間を超えるテストに対してバグをファイルします。
機能テストのガイドライン
機能テストは独立している必要があります。 L2 テストの主な概念は分離です。 適切に分離されたテストは、実行環境を完全に制御できるため、任意の順序で確実に実行できます。 状態は、テストの開始時に認識されている必要があります。 あるテストでデータが作成され、データベースに残された場合、別のデータベース状態に依存する別のテストの実行が破損する可能性があります。
ユーザー ID を必要とするレガシ テストでは、ID を取得するために外部認証プロバイダーを呼び出した可能性があります。 このプラクティスでは、いくつかの課題について説明します。 外部の依存関係は、一時的に信頼性が低いか使用不能になり、テストが中断される可能性があります。 この方法は、テストの分離の原則にも違反します。これは、テストによって ID の状態 (アクセス許可など) が変更され、他のテストで予期しない既定の状態になる可能性があるためです。 テスト フレームワーク内で ID のサポートに投資して、これらの問題を防ぐことを検討してください。
DevOps テストの原則
テスト ポートフォリオを最新の DevOps プロセスに移行するために、品質ビジョンを明確にします。 Teams は、DevOps テスト戦略を定義して実装する際に、次のテスト原則に従う必要があります。
左にシフトして前にテストする
テストの実行には長い時間がかかる場合があります。 プロジェクトの規模が大きくなるにつれて、テストの数と種類が大幅に増加します。 テストスイートが拡大して完了までに数時間または数日かかる場合、それらの実行は最後の瞬間まで延期されることがあります。 テストのコード品質の利点は、コードがコミットされるまでは実現されません。
実行時間の長いテストでは、調査に時間がかかるエラーが発生する場合もあります。 Teams は、特にスプリントの早い段階で、障害に対する許容度を構築できます。 この許容範囲は、コードベースの品質に関する分析情報としてテストの価値を損ないます。 コードを出荷可能にするには不明な量の技術的負債を支払わなければならないため、長時間かかるギリギリのテストは、スプリント終了時に予想外の事態を引き起こすことがあります。
テストを左にシフトする目的は、パイプラインの前の方でテスト タスクを実行して、品質をアップストリームに移動することです。 テストとプロセスの改善を組み合わせることで、左にシフトすることで、テストの実行にかかる時間と、サイクルの後半での障害の影響の両方が軽減されます。 左にシフトすると、変更がメイン ブランチにマージされる前に、ほとんどのテストが完了します。
コードの品質を向上させるために特定のテスト責任を左にシフトするだけでなく、チームは他のテストの側面を DevOps サイクルの後半にシフトして、最終製品の品質を改善することができます。 詳細については、「運用環境でのテストにシフトする」を参照してください。
可能な限り低いレベルでテストを記述する
単体テストをもっと作成しましょう。 外部の依存関係が最も少ないテストを優先し、ビルドの一部としてほとんどのテストを実行することに重点を置く。 アセンブリと関連するテストが削除されるとすぐに、アセンブリの単体テストを実行できる並列ビルド システムを検討してください。 このレベルでサービスのすべての側面をテストすることは不可能ですが、より重い機能テストと同じ結果を生成できる場合は、より軽い単体テストを使用するのが原則です。
テストの信頼性を目指す
信頼性の低いテストは、組織で維持するためにコストがかかります。 このようなテストは、自信を持って変更を加えにくくすることで、エンジニアリング効率の目標に対して直接機能します。 開発者は、どこでも変更を加え、何も壊れていないという自信をすばやく得ることができる必要があります。 高い信頼性基準を維持します。 UI テストは信頼性が低い傾向があるため、使用しないことをお勧めします。
任意の場所で実行できる機能テストを記述する
テストでは、テストを有効にするために特別に設計された特殊な統合ポイントが使用される場合があります。 この方法の 1 つの理由は、製品自体のテスト可能性の欠如です。 残念ながら、このようなテストは、多くの場合、内部知識に依存し、機能テストの観点からは重要ではない実装の詳細を使用します。 これらのテストは、テストの実行に必要なシークレットと構成を持つ環境に限定され、通常は運用環境のデプロイは除外されます。 機能テストでは、製品のパブリック API のみを使用する必要があります。
テスト容易性のための製品の設計
成熟した DevOps プロセスの組織は、クラウドの周期で高品質の製品を提供することが何を意味するのかを完全に把握します。 単体テストを機能テストよりも優先してバランスを強くシフトするには、チームがテスト容易性をサポートする設計と実装の選択を行う必要があります。 コーディングスタイルが異なるのと同様に、テスト容易性のために適切に設計され、適切に実装されたコードを構成するものについては、さまざまなアイデアがあります。 原則は、テスト容易性のための設計は、設計とコードの品質に関する議論の主要な部分でなければならないということです。
テスト コードを製品コードとして扱う
テスト コードが製品コードであることを明示的に指定すると、テスト コードの品質が製品コードと同じくらい重要であることが明らかになります。 Teams では、製品コードを扱うのと同じ方法でテスト コードを扱い、テストとテスト フレームワークの設計と実装に同じレベルの注意を適用する必要があります。 この作業は、 コードとしての構成とインフラストラクチャの管理に似ています。 コードレビューを完了させるためには、テストコードも考慮し、それを製品コードと同じ品質基準で評価する必要があります。
共有テスト インフラストラクチャを使用する
テスト インフラストラクチャを使用して信頼できる品質信号を生成するためのバーを下げる。 チーム全体の共有サービスとしてのテストを表示します。 単体テスト コードを製品コードと共に格納し、製品と共にビルドします。 ビルド プロセスの一部として実行されるテストは、Azure DevOps などの開発ツールでも実行する必要があります。 ローカル開発から運用環境まで、すべての環境でテストを実行できる場合、製品コードと同じ信頼性が得られます。
テストの責任をコード所有者にさせる
テスト コードは、リポジトリ内の製品コードの横に存在する必要があります。 コンポーネント境界でコードをテストするには、コンポーネント コードを記述するユーザーにテストのアカウンタビリティをプッシュします。 コンポーネントをテストするために他のユーザーに頼らないでください。
ケース スタディ: 単体テストで左にシフトする
Microsoft チームは、従来のテスト スイートを最新の DevOps 単体テストとシフト左のプロセスに置き換えることにしました。 チームは、次のグラフに示すように、3 週間のスプリント全体の進行状況を追跡しました。 このグラフには、126 週間にわたる 42 スプリントまたは約 2 年半の労力を表すスプリント 78 から 120 が含まれます。
チームはスプリント 78 で 27,000 件のレガシ テストを開始し、S120 ではレガシ テストをゼロに達しました。 L0 と L1 の単体テストのセットは、古い機能テストのほとんどに取って代わりました。 新しい L2 テストによって一部のテストが置き換えられ、古いテストの多くが削除されました。
完了までに 2 年以上かかるソフトウェア体験では、プロセス自体から多くのことを学ぶ必要があります。 全体として、2 年間にわたってテスト システムを完全にやり直す作業は、大規模な投資でした。 すべての機能チームが同時に作業を行ったわけではありません。 組織全体の多くのチームがすべてのスプリントに時間を費やしており、一部のスプリントではチームが行ったことのほとんどでした。 シフトのコストを測定することは困難ですが、チームの品質とパフォーマンスの目標に対する交渉不可能な要件でした。
作業の開始
当初、チームは TRA テストと呼ばれる古い機能テストだけを残しました。 チームは、特に新機能のために単体テストを作成するというアイデアを開発者に求めていました。 L0 と L1 のテストを可能な限り簡単に作成することに重点が置かれていました。 チームは、まずその機能を開発し、勢いを構築する必要があります。
前のグラフは、チームが単体テストを作成する利点を見たので、単体テスト数が早期に増加し始めているのを示しています。 単体テストは保守が容易で、実行速度が速く、障害も少なくなっています。 pull request フローですべての単体テストを実行するためのサポートを簡単に得られました。
チームはスプリント101まで新しいL2テストの作成に集中しませんでした。 その間、TRAテスト数はスプリント78からスプリント101に27,000から14,000に減少しました。 新しい単体テストでは一部の TRA テストが置き換えられましたが、その有用性のチーム分析に基づいて、多くは単に削除されました。
ソース ツリーでさらに多くのテストが検出され、グラフに追加されたため、TRA テストはスプリント 110 で 2100 から 3800 にジャンプしました。 テストは常に実行されていましたが、適切に追跡されていないことが判明しました。 これは危機ではありませんでしたが、必要に応じて正直に言い直し、再評価することが重要でした。
より速く
チームが非常に高速で信頼性の高い 継続的インテグレーション (CI) シグナルを得た後、製品品質の信頼できるインジケーターになりました。 次のスクリーンショットは、実行中のプル要求と CI パイプラインと、さまざまなフェーズを経るまでにかかる時間を示しています。
pull request からマージに約 30 分かかります。これには、60,000 個の単体テストの実行が含まれます。 コードのマージから CI ビルドまでの時間は約 22 分です。 CI SelfTest からの最初の品質信号は、約 1 時間後に発生します。 その後、ほとんどの製品は、提案された変更でテストされます。 Merge から SelfHost までの 2 時間以内に、製品全体がテストされ、変更が運用環境に移行する準備が整います。
メトリックの使用
チームは、次の例のようなスコアカードを追跡します。 スコアカードは、大まかに言えば、正常性または負債と速度の 2 種類のメトリックを追跡します。
ライブ サイトの正常性メトリックの場合、チームは検出する時間、軽減する時間、チームが運んでいる修理アイテムの数を追跡します。 修復アイテムは、チームがライブ サイトの振り返りで識別し、同様のインシデントが繰り返されないようにする作業です。 スコアカードは、チームが修理項目を妥当な期間内に完了させているかどうかを監視します。
エンジニアリングの正常性メトリックの場合、チームは開発者ごとにアクティブなバグを追跡します。 開発者ごとにチームに 5 つ以上のバグがある場合、チームは新しい機能開発の前にそれらのバグの修正に優先順位を付ける必要があります。 チームはまた、セキュリティなどの特別なカテゴリで古いバグを追跡します。
エンジニアリング速度メトリックは、継続的インテグレーションと継続的デリバリー (CI/CD) パイプラインのさまざまな部分で速度を測定します。 全体的な目標は、DevOps パイプラインの速度を上げることです。アイデアから始めて、コードを運用環境に取り込み、顧客からデータを受け取ります。