はじめに
この記事は、ソフトウェアテスト Advent Calendar 2022 の16日目の記事です。
手動でテストしていたシステムに対して自動化を導入するご支援をさせていただくお仕事をしていたり、なるべく簡単にE2Eテストの自動化ができるツールを作ってGitHubで公開していたり(いきなりの宣伝)するのですが、こういったレガシーコードに対して自動テストを導入しようとしたときに、「おおっ、これは厳しいな・・」と思うポイントが幾つかあって結果的に損していることが多いので、
テスト自動化していなくても気にした方がいい、「テスト自動化のためにも、メンテナンス性向上のためにも、プロダクトコードをきれいにしようね」ということを書いてみます。
(そして自動化していきましょうね)
E2Eテストでよくみるケース
とてもとても単純な例として、下記の画面テストにおける例を考えてみます。
年齢の項目にアルファベットを入力したら、エラーが表示されること。
こんな簡単なテストですが、テストコードを書こうとすると難しい事があったりします。
要素に名前が付けられていない・・その1
<label>年齢</label>
<input type="text" />才
こんな(なんの装飾もしていなくて恥ずかしい)入力項目があったとして、テストケースに従うと特に気になることもなくこの項目にアルファベットを入れることができると思いますが、自動化をしようとするとこの年齢の入力項目をHTML上から特定するための情報が必要になります。
id
やname
といった属性が設定されていればidがageのinput
というような見つけ方ができますが、こういった設定がないと画面を構成する無数の要素の中から頑張って探すことになります。
例えば次のように頑張ります
- inputタグでtextタイプのもので、上からn番目にあるもの
- 年齢というテキストが入っているlabelタグの次にあるinputタグ
- xxというidを持つtableタグの中にあるn番目のtrの中にあるn番目のtdの中にあるinputタグ
頑張れば頑張るほど、画面の見た目に変更が入った場合にこれらの特定方法が動かなくなる予感がしますし、全ての項目がこの状況だとテストコードを書く気力は一瞬で消え去りそうです。
入力項目にid
は設定することも多いと思うのですが、JavaScriptフレームワークやローコード/ノーコードツールなどが自動設定したりしてプログラム的に書かない例も出てきているので、テストのためにもそういった名前付けができるようにする(ツールを選定する)ことは気にしておきたいところです。
要素に名前が付けられていない・・その2
<label>年齢</label>
<input type="text" />才<br />
<span style="color:red">数値で入力してください</span>
テストで大事な結果確認ですが、入力項目に比べてこういった表示文字列に名前が付けられていないケースが非常に多いです。
エラーメッセージだけでなく、単純に画面に表示する要素は「見える」ことに意識がいっているのか自動化していない人からすると名前をつけるメリットがないのかもしれませんね。
その1と同様になりますが、自動化をしようとすると頑張ってこの文字列を探さなければなりません。大変ですね。
まずは動的に変わる部分、例えば入力画面の後に出てくる確認画面などで入力した内容を表示するところはテストとしては確認したい重要な場所なので、そういったところから名前をつけるようにしていきましょう。
要素に名前がつけられていない・・その3
テストで操作する対象、確認する対象は、入力項目や表示文字列に限りません。
ボタン、スプレッド形式で表示されているセル、行(クリックして反応するようなもの)など、操作したい要素は多くあります。
こういった箇所にもしっかりとid
を記載しておくことで、特定しやすくなり、テストのメンテナンス性も向上します。
自動化の予定がないなら気にしなくていい・・・?
プログラムを修正するときには製造者も同じように修正対象の要素を特定するために数百〜数千という行の中を彷徨います。
名前がつけられていれば検索機能を使って一瞬で見つけられますが、名前がないとマウスのスクロールホイールをぐるんぐるんして探します。
名前がないと本当にこれでいいのか?と周りも一緒に見たりして間違いないことを確認できる拠り所も一緒に探します。
一個一個の要素の特定にはそこまで時間がかからないかもしれませんが、これが全ての項目に対して行われるととんでもない時間が積み上がりますし、時間がないときには間違ったものを選んでしまうリスクを伴います。
名前を付ける(要素が特定できる情報の付与)ことは、テストの自動化をしやすくするだけでなく、プロダクトそのものの保守性を向上することにつながります。
こんな感じに名前をつけてあげましょう
<label for="age">年齢</label>
<input type="text" id="age" />才<br />
<span id="age-error" style="color:red">数値で入力してください</span>
APIテストでよくみるケース
テストコードを書かなくてもAPIを直接テストするくらいであればできると思いますが、自動化をしていないプロジェクトではAPIレベルでのテストは実施していますでしょうか?
毎回画面を頑張って叩いてのテスト方法では、APIの実行パターン網羅をしようとすると時間的コストも要員的コストもテスト実施失敗確率も爆上がりしちゃうので、取り組んでいきたいところだと思います。
APIのテストは単純にAPIを実行してその返却値を検証するだけの簡単なものですが、APIを単体でテストしようとテストコードを書こうとすると難しい事があったりします。
API実行の前提が複雑
アプリケーションの特性によって適しているアーキテクチャ構成があるので一概に何が正しいとは言えないのですが、画面の遷移の順に依存するようなAPIになっている場合は、APIを単体でテストしようとするとAPI実行前に準備しておくことが増えてしまい、下記のように色々と大変になります。
- API実行前に、事前に実行されておくべきAPIの実行結果を偽装する準備(もしくは実際に実行)が必要になる
- 上記の準備に必要となるAPIに変更が入った場合、その準備処理そのものにも変更が必要となりメンテナンスが大変
また、APIが単体で実行できない設計になっていると下記のようなリスクを含んでいきます。
- 単体で実行できないと、プログラムを作成中に簡単に試すことができないので心理的に「ちょっと作って試す」ことをせずに「まとめて作ってまとめて試す」ことになり、間違った実装をおこなってしまった時の手戻りが大きくなる
- API単体には現れてこない前提があるために設計でも気にするところが多くなり、設計ミスのリスクを含んでしまう
アプリケーションの特性にもよりますが、APIはなるべくシンプルに小さく構成して、単体で実行できるようにした方がテストしやすく、結果的に再利用性も上がり、メンテナンス性も向上します。
UTでよくみるケース
テスト自動化を行なっていないと、ファンクションやコンポーネント単位でのテストはなかなか実施できていないのではないでしょうか。
画面を使ったテストだけで全てのテストを実行しようとすると、分岐処理だけでなく様々な処理の網羅をテストするのにはどうしても実施時間がかかってしまいます。人の手によってテストされることを考えると、ちょっとした操作ミスによりテストのやり直しなども発生してしまいますね。なかなかに精神力のいるお仕事です。
やはり処理分岐の確認や、複雑な計算処理などはファンクションレベルで機械的に実行することにして、それぞれのコンポーネントが正しく結合できていることを上位レイヤのテストで行うように整理したいところです。
ファンクションやコンポーネントに対してのテストは単純に対象のファンクションに引数を与えて、戻り値や出力結果(DBやファイルなど)を確認するだけですが、テストコードを書こうとすると難しい事があったりします。
関数の実行に前提が必要
APIと同様ですが、特に何かしらのフレームワークを利用している場合、テスト対象となるファンクションが実行されるためには、XXがロードされていること、XXクラスに値が設定されていること。。。のようにファンクションに渡される引数以外に必要となる前提があることがあります。
また、フレームワークによっては多量の情報の詰まったオブジェクトが引数として渡されるなど、テスト実施に際して準備が膨大になることがあります。
まとめると
- 処理の中で引数以外に必要な情報があり、テスト実施前に引数以外の情報の準備作業が必要になる
- 膨大な情報が詰められたオブジェクトを処理内で持ちまわしてしまっていて、テスト実施前に必要以上に準備が必要になる(最小限に詰めれば構わないが、その判断も難しい)
このような状態だと、下記のようなリスクを含んでいきます。
- ファンクションの引数に現れてこない前提があるために設計でも気にするところが多くなり、設計ミスのリスクを含んでしまう
- 引数に過剰に情報が渡されると、本来そのファンクションの責務だったことの範疇を超えた実装が簡単にできてしまい処理の複雑化に繋がる
- ファンクションへの入力が分かりにくくなるため、設計も実装も複雑になり、欠陥が埋め込まれやすくなる
APIと同様で、ファンクションもシンプルに入出力を分かりやすくして、小さく複数の処理を含めないようにすることでテストしやすく、結果的に再利用性も上がり、メンテナンス性も向上します。
さいごに
プログラムをきれいにする(書く)ということは、コメントを見やすくというようなことではなく、コードとしてシンプルで読みやすく保つことが重要です。
それは、再利用性、メンテナンス性に繋がります。
コードがきれいであれば、テストしやすい(テストコードが書きやすい)です。
テストコードを書くようになると、無駄なコードが見えてくるので、コードがきれいになります。
きれいなコード、テストしやすいコードを意識して、プロダクトコードを書いてみましょう!
それは結果的にコードやテストの運用・保守性に一役も二役も役立ってくれると思います。