はじめに
この記事ではテスト自動化をPMの立場で実施した経験から、色々と思うことがあったのでその内容をまとめています。
これから自動テストに取り組もうとしているPMの方や、実装担当であってもPMの視点が一部見えると思うので参考にしていただけると嬉しいです。
Webアプリケーション系のシステムエンジニアの覚え書き、かつ業務経験は6年位の中堅?の人間の発言なのでご容赦ください。
また記事内容が「今更自動テストやってみたけど苦労したぜ」という個人の所感をまとめたものであるため、その点も先にご承知おきください。
企業毎のテスト自動化についての温度感
テスト自動化というと言葉自体はかなり昔からあった認識ですが、実際には4,5年前位までは「取り組めるとかっこいいけど中々難しいよね」といった意見が主流だったと思います。しかし最近(ここ2,3年?)は状況が変わってきていて、世のイケてるITベンダは当たり前のように取り組んでいる技術になったように思います。TDD(テスト駆動開発)に代表されるような新しい系の開発手法で実際の開発を行うケースも周知され、筆者の所属するような中々腰の重い企業であっても取り組みがいくつもスタートしている状況です。
そういった流れで筆者もいくつかのプロジェクトでPMの立場でテスト自動化を主導してきました。新しい取り組みを行う際に必ず生じる問題だとは思いますが、テスト自動化を新しい取り組みというのも若干恥ずかしいですが 既存の品質評価の仕組みに対して、自動テストを行った場合の品質評価が十分に問題ないことをプロジェクトを回す中で検討・立証していく必要があり、「なかなかパワーが必要」というのが正直なところです。
筆者の所属する会社では既存のテストは「Excelにテスト仕様書を書いてテスト仕様書に沿ったテストを行い、結果を既存の類似するプロジェクトでとった品質指標やレビュー・分析により評価を行う」というのが大まかなテスト工程の流れです。テスト自動化であっても「大きな流れは変わらず、部分的にテスト自動化独自のテストコード実装などに工程が入れ替わるだけ」くらいの認識で筆者はいたのですが、始めてみるとなかなかそうはいかない実態があるということをひしひしと感じました。詳細は後述しますが例えばテストコードの実装一つとっても、テストコード実装の開発サンプルの作成やガイドラインの整備、テストデータ設計の詳細化や実装されたテストコードのレビューの難しさ等、オーバーヘッドが非常にかかりました。
こうした問題が、テスト自動化に対して先進的な企業では共通部品化されていたり、既存のガイドラインが流用出来たりと1つのプロジェクトにかかる負担を少なくする工夫をしているのかなと思っています。産みの苦しみだと思いますが、もっとさわやか(笑)に実施できると世の中の取り組みをみて妄信していたのも事実で、そうしたギャップは以前からそう変わっていない部分のように感じました。これからテスト自動化に取り組もうとされる方は、まず自社でテスト自動化に対する先行プロジェクトの有無や、先行プロジェクトが有る場合でも先行プロジェクトのノウハウが他プロジェクトが参照できる形で共通化されているか等、気にした方がよいと思います。プロジェクトの規模にもよりますが、あまりコスト的に余裕のないプロジェクトで先人の遺産もなくテスト自動化に突っ込むのは中々厳しいものがあると思うのでご注意ください。
自動テストの品質評価について
ここからは具体的な内容に踏み込んで感想を記載します。
テスト計画の策定にあたってまず悩むのが自動テストのテスト結果をどう評価するかでした。大きくテストの品質を評価する観点として、下記を考えました。
- テスト中に発見した不具合を分析して類似の不具合を修正する
- 自動テストのテストコードをレビューし、テスト観点に不備がないか確認する
- テスト結果を品質指標と比較して妥当性を確認する
実際に自動テストを行ってみるとそれぞれで問題が発生したためそれを共有します。
1. テスト中に発見した不具合を分析して類似の不具合を修正する
まず1からですが、既存のテストではテスト工程で仕様書に則った操作を行い失敗した場合は問答無用で不合格となり、不具合管理表に集計され分析をする流れでした。自動テストでも変わらない認識でいましたが、実際にやってみると不具合の分析が難しくなりました。
どういうことかというと、自動テストでテストに失敗した場合、テストコード自体が間違っているのか、実装が間違っているかの2パターンを考慮する必要があります。このうちテストコード自体の不具合は既存テストでいうテスト仕様書ミスのような括りで管理する予定でいたのですが、テストコードの実装は非常に複雑であるためこれが頻出しました。全てを計上することは難しく、またテスト仕様書ミスは分析上もそこまで重視しない項目であるため、頻出すると分析が非常にやりにくくなりました。結果として従来の不具合管理表ではなく、Gitのコミットログのルールを整備し、不具合やテストコードミスをコミットログ上に分類してもらうことで分析から余分な計上を除外しました。
またこれは統制的な話ですが、テストの失敗に対して実装に不具合があった場合も、テストコードの実装と同じ感覚で修正して計上しない実装者がいたりしたため、そういった修正もGitのコミット先を追いかける必要がありました。Gitのコミットルールや、プルリクエスト等の機能をうまく使っていくことで解決しましたが、そうしたルール作りは0ベースだと想定しないオーバヘッド作業として発生するのでご注意ください。
2. 自動テストのテストコードをレビューし、テスト観点に不備がないか確認する
2について、テスト担当が実装したテストコードのレビューを行い、テスト観点での漏れの有無や、保守を考慮したテストコードの実装になっているかチェックをします。ここに関しては事前の施策としてテストコードの実装方法を保守の観点も含めてガイドを作成していたこともあり、テストコード自体はおおむね想定した品質のものが上がってきました。一方で、テスト観点のレビューは死ぬほど大変でした。
ここについては筆者の思い込みもあったのですが、世の中的な自動テストへの取り組みを色々と調べている中で、自動テストでは既存のテスト仕様書のようなものは作成せず、ある程度のプロジェクトルールでテスト観点や方針を定めてレビューで品質を確保するような方法が一般的なのかな?という認識を持っていました。
そのためテスト仕様書は作成せず、テストとして最低限行うテスト観点をガイドに記しテストコードのレビューを行いました。それでどうなったかというと、テストのパラメタが書かれたJSONファイルとテスト実装を読み解いてテストとしての網羅性が問題ないかを確認する羽目になりました。(地獄のようなレビューのしにくさです)
従来であればテストマトリクスを作りパラメタの網羅性の確認や、細かいデータの境界値、ステータス遷移ごとの確認などを観点ごとにある程度俯瞰して確認できていたものが、全て実装の一部として記載され俯瞰的な見方もできない中でレビュワーがパラメタを突き合わせてレビューを行うのは正気の沙汰ではないです。声を大にして言いたいのは、**ある程度の規模の開発であれば必ずテスト仕様書をかかせましょう!**ということです。
「テスト仕様書でテストとしての十分さを先に確認した後で、実際のテストコードがテスト仕様書にそってもれなく実装されたのかを確認する」ことと、「テストコード自体からテストの十分さを確認する行為」では前者の方が圧倒的に楽、かつ承認者への理解も得られやすいです。(テストコードを頑張ってレビューしたとしても個人に依存した方法であるため今度はレビューの妥当性が問われる)
3. テスト結果を品質指標と比較して妥当性を確認する
3については簡単で、既存の先行プロジェクトの資産がない場合指標がほぼ役に立ちません。従来のテスト指標でかろうじて使えるのはテストの検査網羅度くらいで、エラーヒット率やエラー密度などのその他の指標は前述した集計の問題も相まって流用ができませんでした。こうした品質評価指標が十分に整備されていたり、自動テストを利用する場合の品質保証プロセスが社内で確立されているかが、自動テストの推進にあたって非常に大事な取り組みだと感じています。
自動テストの設計・実装について
ここからは実際の実装などの話もしつつ、ポイントになりそうなことに触れていきます。
大きくポイントは下記の通りです。
- テストデータの設計
- テストファースト
- 自動テストの工数規模
- 保守のしやすい自動テスト
1. テストデータの設計
自動テストではテストデータの設計が非常に重要です。設計するポイントは大きく初期状態で投入されているデータ・テストコードに実行値として渡すデータ・実行結果として期待するデータの3つです。テストデータの設計(またはテスト仕様としての定義)は、自動テストに関係なく本来行うべきものではありますが、自動テストにあたっては前述したレビューの難しさの問題も合わさり、事前にテストデータの十分さをしっかりと確認することが十分なテストを実施できているかの一つの指標となります。ここが十分に定義できていない場合、必ずテストとして不十分なテストケースが発生します。
また別の観点で、**テストの実行の度にテストデータをリセットする仕組みが必要になります。**これはテストを繰り返し実行し、その度に同じテスト結果を得るために必要不可欠な仕組みです。例えばテスト環境の初期データが前回テスト結果の状況を引き継ぐような環境では2度目、3度目のテスト実行が前回と同じ条件にならず失敗し、それにより自動テストの信頼性が損なわれます。
最近は単体レベルであればテストフレームワークがこの機能を備えていることが一般的です。例えばSpringBoot+JUnit5ではSQLアノテーションやTransactionalアノテーションによりテストの実行前にデータの初期化を行うことができ、初期化したデータを次のテスト実行までにリセットできます。
一方で、結合や総合のレベルのテストを自動化した場合、独自の仕組みでデータの初期化を行う必要が出てきます。これはテスト対象が様々なモジュールを結合して稼働させた環境そのものになるためで、多くのテストフレームワークがサポートする範囲を超えるためです。一例として筆者はJenkinsのパイプラインにより、テスト実行前に環境の初期化、およびデータの初期化を行い、その後に自動テストを環境に流すような制御を行いました。テスト実行までの仕組みが大掛かりになり、実行時間も長くなりますが信頼性の高いテスト環境を整えることも重要です。
2. テストファースト
一般的にテストコードを先に書き、そのテストを満たす実装を書くことをテストファーストと呼びます。違ったらすみません
個人的にはテストファーストの実現は、自動テストの実装に慣れたプロジェクトで初めて実現できるレベルの高い開発手法だと思っています。何故かというと自動テストに慣れ親しんでいない人はまず自動テストを先にかけません。また前述したレビューのしにくさを実装担当のスキルレベルにより一部代替することになるため、プロジェクト内で自動テストの実装方法が確立していなければテストファーストは大事な品質的チェックが漏れるだけの、リスクの高い開発手法になると思います。
勘違いしてほしくないのは筆者自身はテストファーストは非常に重要な手法であると認識していることです。何故かというと実装に対して後からテストを書く場合、担当者が実装を正とした自動テストを書くリスクがあるためです。
実装担当は自分の実装が正しいと思ってコードを実装しており、そのままテストコードの作成に取り掛かると、実装の動きを正として本来実施するべきテスト観点が入っていないテストコードや、的外れなデータでテストを行うことがあります。何を馬鹿なと思うかもしれませんが本当によくあります。そうして書かれた自動テストは、レポート上何の問題もなく合格と表示されるため、検知する方法は実装のレビューのみで非常に見つけにくいです。
3. 自動テストの工数規模
自動テストの作成は既存のテスト実施と比べてどれくらいの規模になるのかぱっと出てくる人は自動テストに対して非常に深い知見を持っていると思います。一般的には1.5~3倍くらいの規模になるといわれており、基本的に自動テストがコストメリットになるのは保守の段階や繰り返しプロジェクトが継続する場合のみで、1つのプロジェクトに絞って評価すればだいたいのプロジェクトで自動テストはマイナスのコスト評価になると思います。
※筆者のプロジェクトでは1.5倍位の工数がかかりました。
この工数見積もりのしにくさが自動テストを実プロジェクトに取り入れるための大きなハードルであり、新規に自動テストに取り組む場合は大きなリスクになります。工数規模を見積もる一番の方法は先人のプロジェクトでどれだけの実績値になったかを参考にすることですが、そういった情報がない中では、自動テスト工程で行う作業をできるだけ具体的にし、積み上げを作ることが次善の方法です。
また基本的に自動テストが短期的にコストメリットになることはないので、プロジェクトオーナーが自動テストに理解を持っている必要があります。
技術的な観点では、パラメータテストを上手く取り入れると少ないテストコードの実装で多くのテストパターンを網羅することができ、実質的に工数を削減することができます。パラメータテストは昨今のテストフレームワークでは当然のように具備する機能でもあるため、実装自体は比較的整備されていると思います。ただしあらゆるテストパターンを網羅できるような手法ではないため、テスト対象に合わせて適切なテストコードの実装が必要です。
4. 保守のしやすい自動テスト
「自動テストが書ける」ことと、「保守性のある自動テストが書ける」ことは全く異なります。自動テストの実装は、実装担当者のスキルに依存する部分が非常に大きいため、何のガイドラインもない状況では生産される自動テストは担当ごとにバラバラなものになります。
例えば「1つのテストケースに様々なテストが内包されているテストコード」、「データプロバイダ関数を様々なテストで使いまわし、影響範囲がわからなくなっているテストコード」、「同じテストコードをコピペで使いまわし長大となったテストコード」等々
例に挙げた様々な実装が入り乱れたテストコードの保守は至難を極めます。たいていの場合そうしたテストコードは負債となり、次の機能改修で放棄されるか無視されます。前述したとおり自動テストは継続的な開発において初めてプロジェクト資産となるモノであり、次のプロジェクトに引き継げない自動テストはただの自己満足でやらない方がましです。
自動テストを行う場合、必ず保守性も加味したテストコード実装のガイドラインを整備しましょう。整備する内容はテストフレームワークに依存しますが、大きな観点は使いまわしが可能だと思います。
個人的なポイントを下記に挙げると、
- テスト対象を明確にし、1つのテストは1つの確認を行う(複数のテストを1つのテストで行うと、確認が非常に難しくなります)
- 複雑なオブジェクトの生成はファクトリ関数を作り共通化する
- 可能なものはパラメータテストとして1つの実装とデータリストを作成し、少ない実装で複数のテストを行う(パラメータテストの利点として、同様のテストであればデータを追加するだけでテスト項目を増やせるため、レビューなどで追加のテスト項目があっても対応が容易です)
- データのプロバイダ関数を複数のテストで使いまわさない(データを使いまわすと影響範囲がわからなくなり、修正が難しくなります)
- 実施するテストは簡易でもいいのでテスト仕様書などの確認が容易な資料とリンクさせる
- 結合テストなどで連続した操作が必要な試験は、操作ステップを共通部品として作り、テストコードは操作フローとして複雑な実装と切り離す
- データ初期化などの機能を使い、何回実施してもテスト結果が変わらないテストを作る
等です。
最後に
最後まで駄文を読んでいただきありがとうございました。
具体的な実装例などはあえて触れなかったため抽象的なまとめになりましたが、筆者が自動テストへの取り組みの中で感じたポイントをまとめています。
これから自動テストに取り組もう!と考えている意欲的な方に是非読んでいただき、参考になれば幸いです。
また「ここは違うのではないか?」などの意見もありましたら是非コメントください。参考にしたく思っています。