📝

自動テストを作るかどうかの判断基準

に公開

こんにちは!株式会社PREVENTでiOSアプリエンジニアの佐藤です。
この記事はPREVENTアドベントカレンダーの6日目の記事です。

https://adventar.org/calendars/12152

弊社のiOSプロジェクトで今年、自動テストはどういう基準で作るかどうかの方針を定めたので、具体的にどういう方針なのかを技術的な判断内容を含めて紹介します!

自動テストの定義

ソフトウェア開発での自動テストとは、ソフトウェアテストを手動ではなく自動で実施されるものを言います。
ソフトウェアテストの中でもテストの対象によっていくつか分類できますが、単体テストの考え方/使い方という書籍の分類に倣うと以下3種類に分けられます。[1]

  • 単体テスト:Mockを必要としない純粋なロジックのテスト
  • 統合テスト:Mockや複数のオブジェクトに依存するプレゼンテーションロジックのテスト[2]
  • E2Eテスト:UIテストなどユーザー視点でのテスト

本記事では、上記全てのソフトフェアテストを一括りに自動テストと表現します。

自動テストを作るかどうかの基本的な判断基準

弊社のiOSプロジェクトでは、ソフトウェアへの修正にデグレがないかを素早く検証して、素早く高い品質でリリースできるようにすることを目的に自動テストを実装しています。

目的を達成するために、アプリのすべての挙動を自動テストで検証するとは考えておらず、テストが増えればその分テストのメンテナンスも必要になるので、増えすぎるとかえって開発のスピードが落ち素早くリリースができなくなってしまいます。
また、テスト実装にもコストがかかるため、すべてのケースの自動テストを実装しようと思うと膨大な時間がかかってしまいます。
逆に何回も手動でテストするようなケースに絞って自動テストに置き換えられると、手動テストで行っていたテストが高速で完了するので、めちゃくちゃ嬉しいですしまさに素早く高い品質でリリースするという目的を叶えることにつながります。

そのため、自動テストを実装するかどうかの基本的な判断基準は以下2軸で判断しています。

  1. 対象機能がプロダクトとして重要かどうか(対象の機能を頻繁にテストするかどうか)
  2. テストの実装・保守のコストが低いかどうか

上記2軸で自動テストの重要度を大・中・小で区切ると以下のようになります。

- 実装・保守のコスト低 実装・保守のコスト高
対象機能が重要
対象機能が重要でない
自動テストの重要度 内容
積極的に自動テストを実装したい
できれば自動テストを実装したい
自動テストを実装しない

テストの重要度が大きければ大きいほど積極的に自動テストを実装していくという基本方針を踏まえつつ、ソフトウェアテストの種類に応じて細かい判断基準があります。
以降では、弊社のiOSプロジェクトでソフトウェアテストの分類ごとにどういう判断基準でテストを作るのかの方針について紹介します。

単体テスト

単体テストの特徴として、検証できるコードのカバレッジは少ないですが、Mockが絡まないので比較的テストコードがシンプルになり、テストの実装や保守のコストが低いです。

特徴を踏まえて、新規に実装する機能に関しては、基本的には積極的に実装します。
理由として、実装コスト、保守コストが低いので、実装・保守フェーズでボトルネックになる可能性は低いです。
つまり自動テストの重要度が小になることは基本ありません。
ただあくまで基本的にはという話で、テスト対象によっては複雑になる単体テストケースもあると思うので、そういう場合は機能の重要度や今後も修正が入る可能性が高いかどうかという観点で、テストを実装するかどうかを判断します。

既存の機能に関しては、ViewModelに単体テストで扱うようなロジックが結合してしまっている部分が多いという都合で、短期的には積極的には増やさない方針をとっています。
単体テストを書くには、ViewModelのようなプレゼンテーションロジックに結合した純粋なロジックを切り出す必要があり、リファクタリングという工程が必要になってしまいます。
このような事情で、単純にコストが高くつくのと、ViewModelのテストすらない場合は、リファクタリングを行った時にデグレを検知を手動テストに依存することになり、さらにコストが高くつくため、短期的には既存機能に単体テストは増やさないようにしています。

ただ中長期的には、プロダクトとして重要な機能に関しては、単体テストを増やしていきたいと考えています。
リファクタリングによるデグレのリスクを少ない工数で抑えるために、統合テストもしくはE2Eテストを備える必要があるので、短期的には難しいと思いますが、将来的には進めたいということで中長期的としています。

統合テスト

統合テストの特徴として、複数のロジックやMockを必要とするようなプロセス外依存[3]を扱うオブジェクトを含んだ処理を検証するため、テストコードが複雑になりやすく、テストの実装や保守は比較的コストが高いですが、検証できるコードのカバレッジは高いです。(単体テストと逆ですね)

特徴を踏まえると、統合テストは多くのテストを実装するのが大変ですし、その後の保守はもっと大変になるので、単体テストに比べてあまりケース数は増やしたくないテストになります。
テストの重要度で言うと、中・小に該当するケースが多いため、重要度が小に入っていないかどうかはよりシビアに都度検討する必要があります。

新規に実装する機能に関しては、テスト対象の設計を一から作れる点やテスト対象コードを熟知している点から、テストの実装自体は簡単に行えるケースがほとんどなので、積極的に実装することが多いです。
ただ、バリエーションを網羅するようなテストケースの作り方はせず、ハッピーケースを1ケース実装し、細かいバリエーションのケースは単体テストで検証するようにします。

既存の機能に関しても、同じようにテストの重要度を見て判断します。
ViewModelを例にすると、DIできる設計になっておらず、シングルトンオブジェクトに依存しているような設計になっていると、リファクタリングが必要になり実装コストが高くついてしまうので、機能の重要度を見て本当に実装する必要のあるケースなのかは判断する必要があります。

E2Eテスト

E2Eテストの特徴として、基本的にプロセス外依存もMockを使わず本物の依存を使ってテストする都合上、テストを実行するための環境を整えるのが難しく、かつテストの実行時間も単体テスト・統合テストと比べるとかなり長いです。しかし、ユーザーが触る環境とかなり近い状態でテストを行うことができ、検証できるコードのカバレッジが最も高くなります。また、コードの設計に依存しないテストなので、テスタビリティの低いコードでも自動テストが行えます。

E2Eテストはアプリの広い挙動を検証するため、挙動を変えるたびにE2Eテストの内容も変えることになりメンテナンスは比較的大変です。
E2Eテストを実行するのが技術的に難しいもしくはできるけど工数がかかる(開発用機能を実装するなどで)こともあり、そういった場合はE2Eテストでの検証は諦めて、単体テスト・統合テストで代用できないか、もしくは自動テスト自体を諦めるかを判断します。
このような特徴を見ると、自動テストの重要度は基本、中or小になると思います。

機能が重要でなければ、ほとんでのケースで自動テストの重要度は小になるでしょうし、重要であればテストケース追加のコストが現実的かどうかによって、自動テストの重要度は中or小になると思います。

例外として、既存機能においては、重要な機能かつ設計上単体テスト、統合テストの実装にリファクタリングを要するところは、短期的な戦略としてE2Eテストの重要度を高く置いています。(テスト実装が高すぎるケースは、E2Eテスト実装は見送りました)
リファクタリングによるデグレのリスクを自動テストで早期に簡単に検知できるようにして、全体的な工数を抑える為です。

まとめ

弊社のiOSプロジェクトで自動テストを実装するかどうかの判断基準について紹介しました。
基本方針として、テスト対象の機能の重要度とテスト実装・保守にかかるコストの2軸で、コスパの観点で自動テストが素早く高品質なリリースが行えるかという基準で判断します。
ソフトウェアテストの種類によって様々な特徴があるので、そういった特徴も考慮して、最終的に自動テストを実装するか判断します。
あくまで、1方針例になりますが、テスト戦略を検討する上で参考になりましたら幸いです。

脚注
  1. 参考書籍では統合テストの一種としてE2Eテストが紹介されていたりしましたが、各テストの分け方や意味は多少筆者の解釈でアレンジしています。 ↩︎

  2. MVVMでいうところのViewModelのテストケースに相当します。 ↩︎

  3. モバイルアプリ視点だと、バックエンドサーバーなどアプリのプロセスの外にある依存のことを指します。 ↩︎

Discussion