ソフトウェアテストと聞いて、まず思い浮かぶのは何でしょうか。 開発者であれば、単体テストと結合テストを真っ先に挙げる人が多いでしょう。 どちらのソフトウェアテスト方法も、高品質の本番環境コードベースを開発し、保守するうえで欠かせません。 とは言え、この 2 種類のテストだけでは不十分です。
テスト作業とは、アプリケーション全体を評価し、アプリケーションが適切に機能している間はその動作を大局的に観察して、逸脱があれば警報を発するものである必要があります。 機能テストと非機能テストの両方を行ってこそ、各アプリケーションレイヤーの信頼性を高める包括的なソフトウェアテストプロセスが実現します。
機能テストとは?
機能テスト(Functional Test)目的は、一連の要件や仕様に照らし合わせてアプリケーションの機能をチェックすることです。 通常は、基盤となるコードにテストスクリプトを含めて実施します。
実際の出力と期待される動作を比較するので、個別のモジュールを隔離してテストする場合に比べて全体の状況をはっきりと確認できます。 エラーが発生しやすいポイントは、モジュール間のやり取りです。
機能テストには数多くの種類があります。以下にその例を示します。
- 単体テスト (非機能テストにも利用可能)
- 結合テスト
- ユーザー受け入れテスト
- クローズドボックステスト
単体テスト
たとえば API アプリケーションの単体テストでは、テスト環境にデプロイしたシステムにリクエストを行い、応答をドキュメントと比較します。 しかし、単体テストには限界があります。 アプリケーションの機能が仕様と異なる、機能のデグレードが発生しているといった違いは単体テストでは検出できません。こうした違いは、通常、アプリケーション全体にわたる機能テストで検出します。
結合テスト
結合テストでは、ソフトウェアモジュールの連携動作を検証します。 コードを結びつきのゆるいモジュールとして開発する場合 (通常はこの方法が望ましい)、コンポーネント間のやり取りは明示的なコントラクトに依存します。 結合テストでは、ソフトウェアの各コンポーネントがコントラクトの終わりまで存続するか検証し、やり取りによってデグレードが生じる場合に警告を生成します。
ユーザー受け入れテスト
ソフトウェアのユーザー受け入れテスト段階では、エンドユーザーまたは代表者にアプリケーションの一部または全部を提供して、実際の操作や機能をモデル化します。 一般に、健全なエンジニアリング文化では、ユーザー受け入れテストに頼りすぎないことが推奨されています。ユーザー受け入れテストは信頼性が低く、コストと時間がかかるからです。 そうは言っても、ほとんどのアプリケーションのテスト手順にユーザー受け入れテストは欠かせない要素であり、何らかの形で実施されています。
クローズドボックステスト
機能テストでは、完成したアプリケーションのクローズドボックステストも行うことがあります。 クローズドボックステストでは、アプリケーションの内部の処理は考慮せず、出力を総合的に確認します。 クローズドボックステストは、非機能テストを補完する機能テストとしてよく使われます。 最外レイヤーとやり取りするコードを自動的にテストし、出力だけを評価します。 API テストだけを必要とするアプリケーションなら、コードで API 呼び出しを行い結果を評価するだけなので、クローズドボックステストのプロセスは簡単です。
しかし、ユーザーインターフェースを備えたアプリケーションでは、クローズドボックステストは複雑になります。 この複雑さへの対処方法の 1 つは、Selenium などの高度なテストツールを使うことです。 この種のツールを使うと、Web ブラウザーでのユーザーの操作をマネしてコードでアプリケーションを操作できます。これにより、ユーザー受け入れテストを自動化すると同時に、テストの信頼性を高めと規模を拡大できます。 しかし、ユーザーインターフェースにクローズドボックステストを適用すると、このテストならではの課題が絡む複雑な問題が生じます。 チームのエンジニアリング能力がすぐに消費されてしまうのです。
非機能テストとは?
非機能テストの評価対象は、機能上は不可欠ではないが、エンドユーザーのエクスペリエンスに影響するアプリケーションの特性です。 たとえば、高負荷時のパフォーマンスと信頼性はソフトウェアシステムの機能的要素ではありませんが、ユーザーエクスペリエンスに影響することは間違いありません。 ある特性が非機能テストに合格しなかったからといって、ユーザーにとっての不具合が生じるとは限りません。しかし、システム (特に大規模なシステム) 内に問題が存在する可能性があります。
非機能テストには数多くの種類があります。以下にその例を示します。
- パフォーマンステスト
- 負荷テスト
- ユーザビリティテスト
- セキュリティテスト
パフォーマンステスト
非機能テストで欠かせないプロセスの 1 つが、パフォーマンスに関するものです。 パフォーマンステストでは、リクエストに対するソフトウェアシステムの応答が高速かどうかを確認します。 応答が遅いと、ユーザーエクスペリエンスが損なわれます。適切なパフォーマンステストを作成すれば、多くの場合、ユーザーに影響が出る前に問題を検出できます。
負荷テスト
負荷テストは、非機能テストの 1 種です。 1 秒に 1 件のリクエストを処理するときと、1 秒に 10,000 件のリクエストを処理するときで、同じパフォーマンスを発揮できるシステムはほとんどありません。 負荷テストでは、システムがピーク時の負荷を処理できるかどうか、ワークロードの急増に対処できるリソースがないときには適切なエラー処理が行われるかどうかを検証します。
ユーザビリティテスト
ユーザビリティテストでは、ユーザーエクスペリエンスの質を測定します。 ユーザビリティテストのほとんどの手順は手作業なので、大規模に行うことは困難です。 しかし、ユーザービリティテストを実施しないと、インターフェースがわかりづらく、直感的に操作できないものになるおそれがあります。アプリケーションのローカライズ時はなおさらです。 ソフトウェア開発プロセスに、この種のテストを実施するための時間をある程度組み込んでおくとよいでしょう。
セキュリティテスト
セキュリティテストでも、一連の非機能要素をテストします。 アプリケーションは定期的にテストして、セキュアでありデータの処理が適切であことを確認する必要があります。 セキュリティテストの内容は、アプリケーションが関係する潜在的な脅威のレベルに応じて、自動スキャンから定期的な侵入テストまでさまざまです。 セキュリティテストをテストスイートの一部とみなすチームはあまりありませんが、 セキュリティテストをテストスイートに含め、単体テストと同様に真剣に取り組むことをお勧めします。
機能テストと非機能テストの比較
機能テストはコードが期待どおりの処理をしていることを確認するテストであり、非機能テストは期待どおりのパフォーマンスであることを検証するテストです。 どちらのテストにも、フロントエンドとバックエンドの要素と動作を検証する手法があります。 テストの種類の中には、機能テストと非機能テストの両方の用途で使用できるものがあります。
機能テストは、非機能テストよりも必要性が高いカテゴリです。 ソフトウェアソリューションは、対象の問題を解決することが第一です。 したがって、ほとんどの場合、実装の詳細や非機能テストで扱われるパフォーマンス指標は、二次的な改善対象とみなされます。 ただし、入念なテスト手法であれば実装やパフォーマンスの要素も考慮されます (特にスケーリングが優先事項の場合)。
機能テストは一般的に、作成と保守にかかるコストが高く、実行に時間がかかります。 機能テストのうち、最もよく使われるのが単体テストです。テスト駆動開発に役立ち、自己文書化コードの作成につながるからです。 素早く実行でき、コード要件の変更にあわせて簡単に更新できるというメリットもあります。 クローズドボックステストなどの手法は、Selenium といった最新ツールを使用しても作成と実行に長い時間がかかります。
テストを選択するうえで最も自然なアプローチは、機能テストと非機能テストを総合的に組み合わせることです。 組み合わせ比率の参考として、”テストピラミッド” がよく使われています。このピラミッドでは、作成と実行にかかる時間の短い単体テストの比率を高めるように推奨されています。
ピラミッドを登るにつれ、実装するテストの量は少なくなります。ピラミッドの中層では結合テストなどの手法、上層ではユーザー受け入れテストなどの手法を実装します。 つまりこのピラミッドでは、実装コストに対する見返りが少ないテスト手法ほど実装する比率を減らし、上側に配置するのです。
また、継続的インテグレーション & 継続的デプロイ (CI/CD) パイプラインに機能テストと非機能テストを組み込むと、テストプロセスの実装と保守にかかるコストを削減できます。
おわりに
ソフトウェアアプリケーションのテストに万能のソリューションはありません。 機能テストの方が非機能テストよりも優れている (あるいはその逆)、というような意見は柔軟性に欠けるといえるでしょう。
非機能テストは、機能テストと同様に不可欠なものです。 しかし、劇的な改善にはつながらないことから、優先順位を低く設定されがちです。 パフォーマンスが低いと、ユーザーは困るものの、ソフトウェアアプリケーションを利用することはできるからです。
しかし、機能が壊れていると、ユーザーは必要な機能をまったく使うことができません。 そのうえ、機能テストは実行時間が短く、コストが少ないことも相まって、多くのチームでは機能テストがソフトウェアテストプロセスの基本となっています。
しかし、非機能テストも重要な役割を果たすので、テストプロセスに組み込む方法を探ることをお勧めします。 テストスイートを優れたものにしたいなら、パフォーマンスやユーザビリティといった非機能的な特性や要素を多岐にわたって検証しましょう。
テストの詳細や、推奨されるテスト手法を継続的インテグレーションツールと組み合わせる方法に興味がある方は、CircleCI と統合できるテストツールの一例をご覧ください。