CI/CD のための自動テスト

継続的インテグレーションと継続的デプロイメント(CI/CD)は、開発チームが機能するソフトウェアをユーザーに頻繁に提供し、それによって価値の提供と製品が実世界でどのように使用されているのかに関する有用なフィードバックを得ることを可能にするを狙いとしています。 多くの組織では、このDevOpsの手法を競争力を維持するための方法として取り入れています。

ところが、より早く提供しなければならないというビジネスの圧力によって、制作されるものの品質が劣ってしまってはいけません。 次の斬新な機能を要求しているユーザーでも、結局のところは、安定して機能するソフトウェアを期待しているわけですから、 最新のビルドに自信を持てる、信頼性が高い徹底した自動テスティング プロセスが継続的インテグレーションデリバリープラクティスに欠かせないのはそのためです。

CI/CD テストを自動化すべき理由

ソフトウェアの品質を確保するにはテストすることが基本であり、テストはソフトウェア開発手法の一環として組み込まれてきました。

ウォーターフォール式のプロセスでは、コードが開発されて統合されると手動テストまたはQAステージに進み、その目的はアプリケーションが仕様どおりに動作するかどうかを確認することでした。

この直線的なアプローチではリリースプロセスが減速し、開発者は、ビルドしたものがその下流のずっと先に進むまで、意図したとおりに機能するのかどうかを確認することができません。それまでには、そのビルドを土台にさらに製作が続いています。 一方、CI/CD 自動プロセスでは、迅速にフィードバックを提供し、更新を少しずつ頻繁にリリースできる、短くてインタラクティブ性の高いサイクルを採り入れたアジャイルアプローチが可能です。 この短く、インタラクティブ性に優れたサイクルでは、新しいコードが機能し、ほかの機能に悪影響をもたらしていないことを自動的に確認するテストが要となります。

継続的インテグレーションには、コード変更を定期的にマスターやトランクにコミットし、そのたびに該当する場合はビルドを実行してソフトウェアの品質を保証するステップが伴います。 CIのメリットを本来のとおり引き出すには、チームメンバーが少なくとも毎日変更をコミットするようにしなければなりません。 しかし、小規模のチームでさえ、このレベルの自動テストを手動で実施するとなれば、QA エンジニアにとっては大きな負担となり、かなり反復的な作業にもなります。 そこで利用できるのが、自動テストです。

自動テストは反復作業に最適で、手動プロセスよりも一貫した結果を得ることができます。人の手では細かい情報を見逃すリスクが避けられなかったり、同じ作業を繰り返す必要がある場合にチェックの実行に一貫性がなくなったりするためです。

自動テストは、同じようなテストを手動で実行するより素早く完了できるだけでなく、並行して実行できるため、時間が重要な要素である場合には、品質管理をスケールアップ(インフラストラクチャが許す限り)することができます。 自動テストを作成するには最初に時間を掛ける必要がありますが、チームメンバーが定期的に変更をコミットし、はるかに頻繁に本番にリリースできるようになれば、その投資した時間をすぐに取り返せます。

自動テストによって多くの面倒な反復作業がなくなるにしても、QA エンジニアが不要となるわけではありません。 QA チームは関連するケースを定義して優先順位をつけるだけでなく、自動テストの作成に関与し、多くの場合、開発者とのコラボレーションにも関わっています。 後の方で説明している自動化できない部分にも、エンジニアの存在が必要なのです。

CI/CDプロセスのどこにテストが組み込まれるのか

テストは、パイプラインの複数のステージで実施されます。

継続的インテグレーションとデプロイメントにあまり詳しくなければ、やり過ぎに聞こえるかもしれませんが、チームが問題をできるだけ早期に見つけられるようにした短いフィードバックループを得ることが CI/CD と QA 自動化の狙いです。

課題が紛れ込んだコードを土台にさらにコードが記述されないようにできるため、課題が見つかった直後にそれを修正する方がより簡単になります。 また、次の作業に進めたことで前の作業の内容を忘れてしまう前に変更を行うことがより効率的でもあります。

多くの自動ビルドテストツールでは、CI/CD ツールとの統合がサポートされているため、ステージごとにテストデータをパイプラインに投入してテストを実施し、ステップごとに結果を確認することができます。 使用するCIツールによっては、前のステップのテスト結果に応じてビルドを次のステージに移行するかどうかを選択することもできます。

自動テストを通過するパイプラインを活用する場合、一般的には最速のテストが最初に実行されるようにビルドテストの順番を決めるのが理にかなっています。 フィードバックをより素早く得ることができるため、テスト環境をより有効に使用することができます。実行時間の長い、より複雑なテストを実行する前に、最初のテストの合格状況を確認することできるからです。

自動テストの作成と実行の両方をどのように優先させるかについて検討する際は、テストピラミッドを考察すると良いでしょう。

テストピラミッドの作成

テストピラミッドは、CI/CD パイプラインの継続的インテグレーションテストを相対的な実行数と順番の観点から優先順位をつける方法を概念化できる便利な手法です。

もともとMike Cohnによって定義された手法で、底部にユニットテスト、中間にサービステスト、そして上部にUIテストが挿入されています。

命名が正確ではないかもしれませんが、素早く簡単に実行できる自動ユニットテストを強い基盤として始め、記述がより複雑で実行により長い時間のかかるテストに進み、最後に数の少ない最も複雑なテストを実施するという基礎が確実に見られます。 では、どのような CI/CD テストを考慮すべきなのでしょうか。 オプションを探ってみましょう。

ユニットテスト

テストピラミッドの基盤には、ユニットテストが最適です。 これらのテストは、できる限り最小規模の動作を解決して、コードが期待通りに機能することを保証できるように設計されています。 ユニットテストの作成に時間を投資したチームでは、開発者がコードを書きながらユニットテストの作成を担当するのが一般的です。 テスト駆動型開発(TDD)を実践している場合は当然の作業ですが、ユニットテストの作成に関して言えばTDDは必要ありません。

既存のシステムで作業している場合やユニットテストを行ったことがない場合は、最初からコードベース全体に使用するユニットテストを作成するのは乗り越えがたい障壁となることが多くあります。 ユニットテストではカバレッジを広げることが推奨されますが、まずは小さな部分から始めて、後でカバレッジを追加していくと良いでしょう。 現実的な戦略としては、作業しているコードをユニットテストに追加すれば、開発中に、すべての新しいコードをカバレッジに含み、操作しているものに基づいて既存のコードの優先順位を決められます。

インテグレーションテスト

インテグレーションテストでは、アプリケーションコードとデータベースの連携など、ソフトウェアの様々な部分が意図するとおりに連携していることを確認します。 インテグレーションテストは幅広く、または詳細に分割すると良いでしょう。 詳細なインテグレーションテストでは、別のモジュールとの連携を実際のモジュールではなくテスト用のダミーを使いますが、幅広いインテグレーションテストでは、実際のコンポーネントまたはサービスを使用します。

プロジェクトの複雑さと関連する内部と外部のサービスの数によっては、幅広いインテグレーションテストよりも素早く実行する(システムの他の部分を利用する必要がないため)詳細なインテグレーションテストを作成し、その後に、優先順位のより高いシステムの領域を対象とした一連の幅広いインテグレーションテストを実行することもできます。

エンドツーエンドテスト

フルスタックテストとしても知られるエンドツーエンドテストでは、アプリケーション全体を確認します。 このテストはGUIを使って実行することも可能ですが、必ずしもそうとは限りません。API呼び出しもシステムの様々な場所で行われます(APIはインテグレーションテストでテストすることもできます)。 エンドツーエンドテストは実行時間が長いだけでなく、不安定になりがちであるため、テストピラミッドでは、エンドツーエンドテストの数を少な目にするように推奨しています。

ユーザーインターフェースに変更があるとテストがうまく実行できなくなり、ビルドテスト結果に不要な雑音が生じたり、テストの更新に時間を掛ける必要が出てきたりする可能性があります。 エンドツーエンドは、低レベルのビルドテストにすでに含まれているものを把握したうえでよく注意して設計することで、最も有益な価値を提供できるようになります。

パフォーマンステスト

テストピラミッドではパフォーマンステストについて触れられていませんが、安定性と速度が主な要件となっている製品については特に、自動テストスイートにパフォーマンステストを含むことをお勧めします。

パフォーマンステストには一般的に、ソフトウェアが本番環境でどのように動作するかをチェックするための多様なテスト戦略があります。 アクセスが増加したときのシステムの動作をチェックする負荷テストや、期待される使用方法を故意に超過して行われるストレステスト、また高い負荷が連続的に発生する環境でのパフォーマンスを測定するソークテスト(耐性テスト)などがあります。

このような様々な種類のテストを使うのは、ソフトウェアが規定のパラメーターに耐えられることを確認するためだけでなく、パラメーターを超過したときにどのように動作するのか(クラッシュするのではなく、体制良く失敗するのが理想的)をテストするためです。

テスト環境

パフォーマンスとエンドツーエンドテストには、本番に非常に似た環境が必要です。ビルドテストデータも必要な場合があります。 テスト中のソフトウェアに対する確信を持つための自動テストにおいては、ビルドテストが毎回同じように実行できること、そして実行用のテスト環境が常に一貫していることが重要です(本番に変更が適用された場合には、それと同じ環境になるように更新する必要があります)。

環境を手動で管理するには時間が掛かるため、このステップを自動化し、新しいビルドのたびに本番前環境を取り崩して作り直せるようにすることをお勧めします。

フィードバックの利用

CI/CDパイプラインの一環として自動テストを実行するのは、適用したばかりの変更に対するフィードバックを迅速に得るためであるため、フィードバックに耳を傾け、それに対応することが不可欠です。

CIサーバーは、すべての結果を1か所で確認できるように、通常は自動テストツールと統合されています。 開発チームは、最新の結果を表示するダッシュボードまたはラジエーターディスプレイと、Slackなどのコミュニケーションプラットフォームへの自動通知機能を合わせて、最新ビルドの動作状況を常に把握できるようにしています。

テストに失敗すると、コードベースのどの部分がテストに関連しているかを把握し、スタックトレース、出力値、またはスクリーンショットなど、テストが提供する情報を閲覧できるにようにしておくと、根底的な原因にたどり着くプロセスを加速化できます。 テストごとに 1 つのものをチェックできるように慎重にテストを設計し、何に失敗したかがわかるように具体的にラベル付けすると良いでしょう。 また、テストの失敗に関する追加情報を提供する継続的インテグレーションテストツールと CI ツールを使うと、ビルドをより早く正常な状態にしやすくなります。

当然ながら、ツールと手法は、CI/CD を実現する上でのほんの一部にすぎません。 非常に優れた CI/CD 自動化手法には、自動 CI/CD テストの価値を認めるだけでなく、ソフトウェアをデプロイできる状態に維持するにはテストの失敗に素早く対応することが重要であることも理解しているチームカルチャが必要です。

CI/CD と自動化テストで手動テストは不要になりますか?

CI/CDをまだよく理解していない人たちの間で、自動テストによって手動テストの必要性だけでなくプロの QA エンジニアの必要性もなくなってしまうのではないかという、誤った考えがよく聞かれます。

CI/CD 自動化によって QA チームメンバーの時間に余裕ができることは確かですが、メンバーが不要になるということではありません。 エンジニアは反復作業に時間を費やす代わりに、関連ケースの定義や自動テストの作成、さらには探索的テストの発想や工夫に集中できるようになります。

コンピューターが実行できるように慎重にスクリプト化された自動ビルドテストとは異なり、探索的テストには凝り固まった制限はありません。 探索的テストは、計画的に構造化されたテストが見逃すエラーを探すことに目的があり、 本質的に、まだ考察しておらず、テストケースが作成されていない課題を見つけるためのテストなのです。 どの領域を探索すべきか決断する際は、新機能と、本番で何かがうまく動作しなかった場合に一番危害を与える分野の両方を検討しましょう。

探索的テストは、毎回、同じ一連のテストを実行することを目的としていないため、手動の反復的なテストで使用されるべきではありません。 探索的テストの最中、また修正する際に課題が検出された場合、自動テストを作成しておけば、課題が再び出現したときに、プロセスのさらに早い段階で検出できるようになります。 テスターの時間を有効に活用するために、手動テストはすべての自動テストが合格となってから実行するようにしましょう。

テスト自動化の継続的な改善

自動テストは、CI/CDパイプラインの中心的役割を果たします。

自動テストを作成するには、時間と労力が必要となりますが、迅速なフィードバックを得られるようになること、そしてコードのデプロイメント可能性を確認できるようになることを考慮すれば、自動テストに高い価値があると言えます。 ただし、テストスイートは作りっぱなしにするわけにはいきません。

自動ビルドテストは、ほかのコードと同じように、アプリケーションの一部です。そのため、関連性と有用性を維持するために、メンテナンスを行う必要があります。 つまり、コードに適用する継続的な改善は、テストにも適用されるのです。

自動テストに新機能向けのカバレッジと探索的テストの結果をを組み込み続ければ、テストスイートの効果と有効性が維持されます。 また、どのように性能を発揮しているのか、またフィードバックをより素早く得るにはプロセスのステップの順番を入れ替えたり崩したりした方が良いのかを検討する時間も作ると良いでしょう。

CI ツールにはパイプラインの最適化に役立つさまざまなメトリクスが用意されていますが、「Flaky なテスト」インジケーターは根拠のない自信や懸念をもたらすような信頼性の低いテストについて警告します。 メトリクスを使って自動テストプロセスを改善することはできますが、テストカバレッジそのものが目標と勘違いしないようにしましょう。 本来の目標は、機能するソフトウェアを定期的にユーザーに提供することです。 自動化は、ソフトウェアを本番にデプロイする際に確信を持てるように、迅速で信頼性の高いフィードバックを提供することで、この目標をサポートしています。