💤

テスト自動化を成功させたいなら可読性を重視すべし

2023/12/16に公開

あんまりテストコードそのものについてかくあるべしみたいな記事とか本って多くない気がしてます。公私問わず10年以上テストコードを書き続けてると色々わかって来たことがあるので、個人的に推せる方法論とか考えを何回かに分けて書いていこうと思います。
今回はテストコードの可読性について書いていきます。

テストコードの可読性

可読性といえば、プロダクトの追加開発や保守のコストを削減するために最も重要な要素であると言っても過言ではないかと思います。でも、可読性とは何でしょうね?これという定義自体をあまり目にしたことがないような気がしています。なので勝手に定義を考えて見ます。私の考える可読性とは目的の情報を獲得のしやすさの度合いかな?と思います。みんな目的の情報を獲得しやすくするために可読性を向上させているもんだと思います。
ではどんな情報を獲得しやすくするべきでしょうか?これまた私見ですが、プロダクトコードの場合はある挙動がどこでどう実現されているのかという情報を獲得したいケースがほとんどだと思います。この情報を最大限に読み取りやすくすることでプロダクトの保守や追加開発は容易になるものだと私は考えています。
さて、テストコードを読む場合はどんな情報を獲得しやすいと嬉しいですかね?色々あると思いますが、ことテストという領域において最も重要なのは以下の3点であると私は考えます。

  1. テスト対象: 何をテストしているか
  2. 因子: テスト対象を動作に影響を与える要素(ここではソフトウェアテストの世界における因子よりも広い意味で使ってます)
  3. 検証項目と因子の関係: テスト対象の挙動が正しいと判断する要素と入力との因果関係

この3点が読みとりにくいテストコードは開発の足を引っ張ります。例えば、テストコードを壊してしまいそうで怖くて触れないからプロダクトコードを理想的な設計修正しながら実装できない、あるいは、どこまでテストされているかわからないからリファクタリングができない、などということが起きたりします。

以下ではこれら3点がなぜ可読性において重要なのか、具体的にどんなことが問題になるのかを説明していきます。

テスト対象

テスト対象が重要である理由は、コードを読むための取っ掛かりがないとコード自体を読み取るのが困難だからです。プロダクトコードの場合でも「どんなツールなのか」「どんな仕様なのか」を全く知らない状態で読むのは非常に難しいでしょう。通常は部分的でも仕様を把握した状態でコードは読み進めていくものだと思います。それと同じでテストコードの場合はテスト対象をある程度把握した状態で読み進める必要があります。

次に、どんなテストコードだとテスト対象がわからなくなるか説明します。テストコードと言っていますが、大抵の場合はテスト関数が1つのテストケースとなっているので、テスト関数と読み替えてもよいです。

典型的な例としてはe2eテストのようなアプリケーションの実際の入出力インターフェース(UIなど)を介してテストするような場合に、そのテストが何の機能をテストしているのかをテストの説明できちんと言及していない場合なんかはテスト対象がわからなくなることが多いです。また、1つのテスト関数でいろんないろんなテストをやりすぎていることでテスト対象が不明瞭になることもあります。

別の例としては単体テストでテスト対象の関数名と関数の処理内容に乖離がある、あるいは、関数名で簡潔に表現することが難しい処理をテストする際に、テストの説明にテスト対象の関数名しか記述していない、といったものもあります。

どちらの例も説明の不十分さが問題となりますが、テストの説明だけでテスト対象を表現するのには限界があったりもするので、一生懸命説明を書けばいいというわけでもないところが難しいところです。この問題に対処する方法も私なりに考えたので別の記事で紹介します。

因子

テスト対象を知ることがコードを読むための取っ掛かりを得ることなのに対し、因子を知ることはそのテスト関数の具体的な挙動やテストケースそのものの必要性を評価することの手助けになります。なぜなら因子はテストにおける肝となる部分だからです。入力の種類によってプロダクトは挙動を変えます。挙動が変わるからこそそれをテストしたいのです。なので、因子が何であるかという情報はプロダクトコードのどんな側面をテストしようとしているのかを知るために非常に重要な手がかりになります。同時にプロダクトコードがどんな条件で挙動を変えるのかを手っ取り早く知るための情報源にもなったりするので、因子がわかりやすいということはプロダクトの理解にも繋がります。

そんな因子がわからない状態とは因子とそうでない値が入り乱れてしまう状態です。例えばRDBにデータを保存する場合は様々な値をカラムに格納する必要があります。レコードのたった1つのカラムにだけ依存して挙動を変えるようなテスト対象の場合はそのカラムだけ入力したいところですが、実際はすべてのカラムに値を入れる必要があり、そのせいでどれが因子でどれが関係のない値なのかわからなくなってしまうといったことがあります。他にも説明が不十分であったりということもありますが、多くの場合は値が入り乱れることが問題になると思います。そして厄介なことに、テストコードで因子とそうでないものを区別することなど基本的にできません。

この因子をわかりやすくするための方法論もまた別の記事で紹介しています。

検証項目と因子の関係

検証という言葉はよくテストと混同されて使われがちです。ソフトウェアテストの世界で検証とはプログラムを動作させて生じた結果が期待したものと一致しているかを確認する行為です。早い話が、テストフレームワークで提供されているassertionexpectationといった関数で実行しているアノアレです。そして検証項目といっているのは、検証する要素ですね。検証項目には比較対象となるプロダクトの動作によって生じた結果とその結果と比較する期待値の両方を指します。

ソフトウェアあるいは関数は入力に対して出力が決定されるため、検証項目と因子の因果関係もしっかりとわかるようになっている必要があります。検証項目だけがただ際立っていたとしても、因子との関係がはっきりしない状態ではプロダクトコードのどんな側面をテストしているのかが不透明になってしまいます。最悪の場合は、因子と検証項目の関係を誤認したまま可読性の低い状態のテストが増え続けていき保守がより困難な状態へと成長していきます。

検証項目と因子との関係がわからない状態の例としては、因子と関係のない要素を検証してしまっている状態が挙げられます。例えば、あるフォームに特定の文字を入れて送信するとバリデーションエラーを発生させるといったテストで、フォームが存在しているかどうかを検証したりするようなテストです。これは因子と全く関係がありません。プロダクトについてよくわかっていない人が読めば「この値を入力するとフォームが出現するってことかな?」などとミスリードしてしまう可能性もあります。

また、可読性とは別の観点で上記のような因子と関係のない検証項目が増えると、テストコードそのものの使い勝手が悪くなっていくという側面もあります。

これらの問題に対処するための方法も別の記事にて説明します。

おわりに

説明したこれらの要素をうまくコードに落とし込むことができれば、テストコードをほとんど読まなくてもそのテストコードを理解できたり、プロダクトコードも理解できるようにもなります。また、この記事だけではどうして行くべきかはそれほどわからないと思うので、他の記事もおいおい書いていくのでそちらも読んでいただければ幸いです。

Discussion