開発フェーズは、ソフトウェア開発ライフサイクル(SDLC)の段階であり、プログラマーがソフトウェアを構成するコードを作成します。アプリケーションのソースコードを作成するだけでなく、データベースの開発や環境のオーケストレーションなども開発フェーズに含まれます。
結局のところ、インフラストラクチャがコードである世界では、コードによるコンピューティング環境の構築は、ソフトウェア開発の他の側面と同じような考え方を必要としています。
開発段階でのテストでは、コードを書いた人がコードをテストするのが原則です。そして、書いたテストは、開発者からコードから離れた後でも自動テスト環境で繰り返し実行できるものでなければなりません。
CI/CDパイプラインの開発フェーズでテストを実施する場合、ユニットテストがスタート地点となります。
ユニットテストは、ソースコードレベルで個別の関数をテストするプロセスです。開発者は、ある関数を実行するためのテストを書きます。関数が実行されるとき、テストはその関数が返す結果に対してアサーションを行います。結果を返さない関数は、ユニットテストの対象としてふさわしくありません。そのような関数は、与えられた関数の範囲を超えてシステムの状態を変化させるからです。したがって、その関数は、インテグレーションレベルあるいはシステムレベルでテストする必要があります。ユニットテストは、うまくカプセル化され、実行時に結果を返す個別の関数を実行することです。
個別の関数をテストするという考え方は、単純で当たり前のように思えるかもしれません。しかし、悲しいことに、1つの関数に多くのアプリケーションロジックを入れすぎているコードが多く存在します。一つの領域をカバーするのではなく、関数があちこちに散らばっているのです。これはスパゲッティコードと呼ばれています。
(図1)スパゲッティコードではユニットテストが困難
ユニットテストに使われる一般的なツールの例としては、JavaコードのテストにJUnit、.NETコードのテストにNUnitとMSTest、NodeJsプログラムのテストにMocha/Chai、PythonコードのテストにUNITEST、PHP用のユニットテストを書くのにPhpUnitがあります。C++は、長い間存在している言語であり、幅広い実装基盤を持っています。したがって、C++をユニットテストするときに、多くのユニットテストフレームワークから選択することができます。C++のユニットテストフレームワークの一覧はこちらで見ることができます。
ユニットテストを書くことをコミットすることは、CI/CDパイプラインにおけるテストのプロセスにおいて不可欠な最初のステップです。しかし、コミットはテストを書くこと以上のものです。書かれたテストは、信頼できるものでなければなりません。テストが信頼できるものであるためには、テストの結果が測定可能でなければなりません。ユニットテストによく使われるメトリクスは、"100%パス” と "コードカバレッジ" の 2 つです。
ユニットテストが実行された時の結果は、パスするか失敗するかのどちらかです。100%パスのテスト指標は、その名の通りですが、コードベースに対して実行されるすべてのユニットテストは、100%パスする必要があります。CI/CDパイプラインのどこかで失敗するテストは、特にそのテストが過去にパスしたことがある場合、直ちに対処する必要があるリスクを生み出します。
100%パスという指標をサポートすることは、ユニットテストの設計方法に直接的な影響を与えます。テストはアサーションに基づきます。実際、テストが有効であるためには、少なくともひとつのアサーションが含まれていなければなりません。しかし、テストに含まれるアサーションの数が増えてくると、厄介なことになります。
通常、大量のアサーションを持つテストはスパゲッティ・テストになる危険性があります。たとえば、validateUserDataTest という名前のテストに 10 個のアサーションがあるとしましょう。多くのアサーションがあるということは、そのテストが関数の振る舞いのさまざまな側面を検証していることを意味します。どれかひとつのアサーションが失敗すると、そのテストはすべて失敗となります。しかし、テストに含まれるアサーションの数が多いため、どの検証で失敗するのかを正確に把握するのは困難です。
ユニットテストのもうひとつのアプローチとして、合理的と判断される場合には様々なアサーションを別々のテストに分割することがあります。開発会社の中には、1 つのユニットテストにつき 1 つのアサーションという方針をとっている会社もあります。また、もっと柔軟に対応する会社もあります。しかし、CI/CDパイプラインで包括的なテストを行うほぼすべての開発会社は、アサーションを適切に分割し、関数の動作の1つの側面のみを検証するように制限することを要求しています。(以下、図2を参照)
コードカバレッジレポートは、記述されたすべてのコードがテストを通じて実行されたことを知るための方法です。最近のユニットテストのフレームワークのほとんどがコードカバレッジレポートを提供しています。以下の図3は、Android Studioでのユニットテストによるコードカバレッジ機能を示しています。
ユニットテストでカバーすべきラインの許容割合は、企業によって異なります。100%のカバレッジを要求する厳しいアプローチを取る企業もあります。しかし、多くの企業では、それほど厳密ではありません。多くの企業では、オブジェクトのコンストラクタのようなデータの初期化関数と同様に、すべてのビジネスルールとアルゴリズムコードがカバーされていることを確認することが、許容できるレベルのカバレッジとなります。
単純なデータオブジェクトを作成する場合、自動コード生成は多くの企業で一般的に行われています。そのため、データオブジェクトのセッターやゲッターのテストは、コードの他の部分で検証されることを信じて見送る企業もあります。
最も重要な2つの項目は、企業が合理的で測定に基づいたコードカバレッジポリシーを実施することと、開発者がCI/CDプロセスにさらされるリポジトリブランチにコードをチェックする前にポリシーを支持することです。
テストがQA部門の唯一の責任であった時代は終わりを告げました。CI/CDパイプラインを通過する必要のある複雑なコードが多すぎるのです。開発者は、エラーを修正するために効率的に対応することができる最も優れた役割を担っています。テストが開発者から離れれば離れるほど、テスト活動のコストが高くなることは過去の研究によって度々示されています。mablの社内ルールは、「書いたら、テストする」です。mablのモットーは、素早く頻繁なテストを実行するために、テストを自動化することです。
自動化されたテストは、CI/CDパイプラインの基礎となるものです。開発フェーズは、CI/CDデプロイメントプロセスの最初のステップです。しかし、それが唯一のステップではありません。CI/CDプロセスの開発フェーズの次のテストフェーズは、ローカルUIテストになりますが、これについては次回に詳しく解説します。
CI/CDパイプライン全体を通してテストを自動化するために、mablがどのように役立つかをご覧ください。今すぐ無料トライアルを開始しましょう