テストの自動化なら CircleCI を活用しましょう。
テスト自動化とは?
テストを自動化するとは、すなわち、どのようなタイミングで、どういったテストが実行される(トリガーされる)のかをあらかじめ定義しておくことに他なりません。テストの実行タイミングは大きく3つに分けられ、それぞれのタイミングで、テスト対象の異なる次のようなテストを自動的に実行させることが可能です。
コードの追加・修正のタイミング
コードの追加・修正のタイミングでは、静的解析やユニットテスト、後述する脆弱性診断の一部が主に自動実行されます。
- 静的解析(lint): 書かれたコードがあらかじめ定義されたルールに従って記述されているのかを解析します。つまり、コードを実行することなく(=静的に)テストを実行します。例えば、段下げはタブなのか、半角空白何文字なのか、未定義であったり初期化されていない変数の使用を禁止するかといったチェックを行います。
- ユニットテスト(単体テスト): ある引数を渡して実際にある関数を実行した結果、正常系・異常系を含め、想定された結果が得られたかどうかを確認します。ソースコードのどの部分がテストされたのか、コードカバレッジとよばれる仕組みと組み合わせて使用することもあります。機能追加時や修正時に正しく動作していた関数が正しく動作しなくなるデグレ(デグレード)の検出において、自動化が有用です。
インテグレーションのタイミング
インテグレーション(サービスやアプリケーションとして実行可能)のタイミングでは、結合テストやE2Eテストが主に自動実行されます。
- 結合テスト(インテグレーションテスト): 前述のユニットテストが関数単位だったのに対し、機能単位(登録、検索、削除など)である機能を実行し、想定した結果が得られたかどうかを確認します。
- E2Eテスト(End to Endテスト): ユーザーインタフェース(UI: User Interface)テストとも呼ばれます。例えば、キー入力やマウス操作、タッチ操作を記録(レコーディング)しておき、画面遷移や表示結果が、機能追加後や修正後にも同様に得られるかどうか、操作記録を自動的に再実行した上で、画面遷移や表示結果を比較することで確認します。
定期的なタイミング
コードの追加・修正が行われている・いないに関わらず定期的に実行されるテストとしては、脆弱性診断が挙げられます。
- 脆弱性診断: クロスサイトスクリプティング(XSS)のような脆弱性は、プログラミング時に埋め込まれる脆弱性であり、コードの追加・編集のタイミングで自動診断されるべきですが、例えば、使用しているライブラリーが自社で開発したものではなく、オープンソースのライブラリーや商用のライブラリーの場合、脆弱性が発見されるとバージョンアップ等のアナウンスが行われます。更新が必要なライブラリーの情報を常にウォッチしておくのは非現実的です。機能追加や修正がまったくない場合であっても、定期的に脆弱性の診断をすることで、既知の脆弱性への攻撃を防止することができます。
テスト対象を理解する
ソフトウェアにおけるテストとは、何をテストしているのでしょうか? ソフトウェアテストのテスト対象を理解する前に、ハードウェアのテストをまず例に取って考えてみましょう。
例えば、自動車であれば(数え方にもよりますが)3万点〜5万点の部品から組み上がっています。これら1つ1つの部品が、求められる外観と、求められる機能が動作するかどうかテスト(検査)され、部品が組み上げられ、製品(自動車)として外観、機能がテストされます。また、製造時(出荷時)の品質だけでなく、出荷後も「カタチ」あるがゆえに、経年劣化が想定(仕様)範囲なのかも重要なポイントです。
ソフトウェアも部品単位のテスト(単体テスト)、インテグレーション後(組み上げ後に相当)のシステムテストを行います。また「カタチ」あるハードウェアと違い、ソフトウェアはソースコードやバイナリーが経年に伴い劣化することはありませんが、出荷後に問題が発見される様な例についても後ほど、ご紹介します。
問題が発生しにくい設計
さて、設計時に品質をいかに作り込むかは、ハードウェアであっても、ソフトウェアであっても重要です。とりわけハードウェアの場合は、ソフトウェアと異なり、製造工程において人や機械(ロボット)に超絶技巧を要求しない、つまり、製造後のテストにおいて、一定以上の歩留まりを確保できるような設計が重要になります。
一方、ソフトウェアでは、設計→コード→単体テストにおいて、部品レベルで仕様通り動作することを検証します。仕様通り動作するかどうかは、テストプログラムを実行することで確認します。単体テスト(ユニットテスト)を効率よく描くためのテスティングフレームワークとしては、JUnitから始まる xUnitとして総称されるフレームワークがしばしば用いられます。
- JUnit- Java向けテストフレームワーク
- Mocha - JavaScript向け(Node.js向け)テストフレームワーク
- RSpec- Ruby向け(Ruby on Rails向け)テストフレームワーク
- PHPUnit - PHP向けテストフレームワーク
- Python unittest ー Pythonが標準で提供するユニットテスト向けモジュール
- NUnit - C#などの.NET言語向けテストフレームワーク
実際にユニットテストを書いてみると、テストしやすい構造(インタフェース)と、テストしにくい構造があることが分かります。ユニットテストにおいては、実装者とテスターを分けるのではなく、同じ人が行うことで、引数の取り方や戻り値の返し方、例外のスロー方法や、クラス、関数の粒度を適切にし、さらには再利用性をより高めることに意識を向けることが可能です。
テスト自動化のメリット
先に挙げたように、テストを自動化するに当たっては、3つのタイミング(コードの追加・修正時、インテグレーション時、および定期的)に実行することで、デグレに代表されるコード品質の低下防止と、E2Eテストで検証されるような、できるはずのことができなくなるといったユーザー体験の低下防止、およびセキュリティ的に強固であることの検証がメリットとして指摘できます。
ユニットテストのメリット
その中でも、ユニットテストを行うことのメリットはなんでしょう? 品質を確保するだけでなく、インタラクティブに何かを入力し、その結果を目視でチェックし、Excelシートに合否を記入するのではなく、1) テストもコードとして記述し 2) 実行結果をファイルとして出力し 3) テストが通らなくても、コードを修正してテストをいつでも再実行できる ことが挙げられます。
また、「テストしやすい構造」という話をしましたが、一旦書いたコードを、テストしやすい構造に修正するのではなく、先にテストコードを書いた上で(テストファースト)、そのテストコードによってテストされるコードを書く、テスト駆動開発(TDD: test-driven development)という考え方も一般的になりつつあります。
確かに、コードを追うことで、そのコードが実際に何をするコードなのかを知ることは容易ですが、知らなければならないのはむしろ、何をさせることを目的として設計されたクラスや関数なのかということなのです。ここが明確でないと(実装から読み解くだけでは)、インテグレーションテストで想定外の動きをした場合に、各モジュールの動作が意図通りなのか、違うのかが不明確になってしまい、解決に時間を要してしまうのです。
まずはユニットテストを自動化しよう
ユニットテストは、開発しているそれぞれのクラスや関数などと対応した形で「コード」として用意されることが一般的です。したがって、コードが変更されたタイミングで「常に」、もっと言えば(少なくとも変更されたコードに関わる)ユニットテストは「漏れなく」実行することが重要です。
「常に、漏れなく」を補償するには、開発者が忘れない様に気をつける、という意識、注意の問題にしてしまうのではなく、GitHubなどのリポジトリ上への登録、更新をトリガーにした「自動化」により、ユニットテストの自動実行を開発プロセスの一部、仕組みにしてしまうことが確実です。
さらに、CircleCIでは、ユニットテストをより早く実行するために、テストの実行結果ファイルに含まれるテスト実行時間を元にして、テストの並列実行や、その際のテスト組み合わせを最適化したり(それぞれの並列実行における所要時間の平準化)、テストのレポート(前述の所要時間に加え、失敗が多いテストのランキングや、成功や失敗が一定しないテストの抽出など)をテスト インサイト ダッシュボード上に表示可能です。
CircleCIでテスト自動化をはじめよう
テスト自動化支援ツールとしての側面から、CircleCIは、「最高のチーム、優れたパフォーマンスを発揮できるチーム」とはどのようなものかを、データに基づいて分析するために、CI/CDワークフローを調査した結果、次の4つのメトリクスを採用しています。
採用されている4つのメトリクス
スループットが高い、つまりワークフローが数多く実行されているということは、デプロイ頻度が高い、つまり、それだけ新たな追加機能、改善された機能が頻繁に提供されているということができます。
次に実行時間について見てみましょう。ワークフローの実行時間が短いということは、テストの結果、問題があることにより早く気づくことができるということに他なりません。ですから、開発者の頭の中にコードの記憶が残っている状態ですぐに修正に取りかかることができます。
その一方で、短すぎる実行時間には注意が必要です。本来は自動化可能な可能なテストが手付かずのまま手動で行われているため、手動テスト時間とのトータルで考えると、実は短くない、つまりエンジニアが問題に気づくのに時間をかけて要している可能性があります。
テストの自動実行は、フィーチャーブランチでの追加・修正の際に、問題の発生を少なくする、また問題発生時に原因をできるだけ局所化する効果があります。その結果、平均修復時間をより短縮し、メインブランチへの取り込み時に成功率を高めることができるのです。
テストの自動化で何が得られるか
CircleCIを活用したテストの自動化により、生み出されるソフトウェアの品質を高めることができるだけでなく、本来のゴールである、お客様や市場が求める新たな機能、改善された機能を品質高く、これまで以上にスピーディーにお届けすることが可能になるのです。
人手でテストを実行しているソフトウェア開発現場には、「ここは問題が起こりにくい箇所だから(時間も限られているし)、今回はテスト実行は省略しよう」という誤った現場力を発揮する余地を残してしまいます。その結果、品質の低いソフトウェアやサービスをリリースしてしまうことで、お客様の信頼を失うことになりかねません。
CircleCIでテストの自動化を始めませんか? CircleCIなら、3つのタイミングでのテストの自動化をすべてサポートしています。