Share via


自動化対象のユニットテスト(単体テスト)の仕様書を書くことは完全なる無駄である

本ブログポストは、マイクロソフトの意見ではなく、私個人の意見であることをお断りしておきます。

DevOps 普及活動の一環として、DevOps ハッカソンというイベントを実施しています。DevOps のプラクティスの一つとしてAutomated Testing (自動化されたテスト) があります。

それに関して複数の参加者の皆さんがこのようなことを言っていました。

「自動テストを書くのは好きではないです。何故かというと、自動化されたユニットテストを書いたら、同じ内容のエクセル方眼紙の仕様書を書かないといけないので、二重に書くのは無駄だし大変だと思うんです。」

はっきり言ってしまうと、このケースの単体テスト仕様書は完全なる無駄であると断言できます。

このポストではその理由をお話ししたいと思います。

1. 単体テストのイメージの違い

この問題が起きている背景には、「単体テスト」というものがCOBOLの時代から変化しているからというのがある。このような仕様書を書くプロジェクトは書く仕様書が定められている形式の開発が行われていると思う。それらの
ルールがどのように醸成されたかを考えてみる。ある意味無理がないところがあるのだ。

 著名なアジャイルコーチの川口氏が言っていたことだが、「一見無駄なルールにも昔には妥当な理由があった」という話がある。COBOL時代の「単体テスト」と、現在の言語のアーキテクチャの「単体テスト」は相当イメージが異なる。

図に示した通り、COBOL時代の単体テストというのは、画面毎に1つのプログラムなので、単体テスト = 画面単位のテストになる。一方最近のアーキテクチャだと、1画面の処理に複数のクラスが協調動作して動く。今の時代でいう単体テストは、画面の単位ではなく、個別の一つ一つの小さなクラスをテストすることを意味する。Unit = 最小の個別の単位なので、相当イメージは違うのだが、どちらも「単体テスト」なのだ。であるので、この記事では、クラス単位のテストをユニットテストと呼び、画面単位でのテストを「画面単体テスト」と呼ぶことにしよう。

画面単位に対してテスト設計をすることは、私は全くの無駄とは思わないが、1つ1つの小さなクラスに対して仕様書を書くのは完全に無駄だ。それに相当するテスト仕様書を書くのは、COBOLの時代だってしていない。

2. 今のアーキテクチャのユニットテスト(単体テスト)を書くことは何故必要か?

従来書いてなかったレベルの単体テストを書くのはなぜ必要なのだろうか?現在主流で使われているJavaや、RubyそしてC#等のオブジェクト指向の言語は、図のように1つの画面の処理を行うときも複数のクラスのインスタンスが協調動作して一つの処理を実施する。V字モデルの話になるのだが、テストを実施するときに、より低レベルのテストを必要十分にテストしておくことは大変重要だ。そのレベルのテストをさぼると、上位のレベルのテストを実施するときに、低レベルのテストまで一緒にすることになり、複雑度がまし、品質の確保が難しくなる。だから、今のアーキテクチャだと昔1つだったプログラムが複数にわかれていて、協調して動くのだから、この「単体」のレベルでしっかりとテストをしておく必要があるのだ。逆にそこを書いておくことで、かつて以上に全体の品質を大きく向上させることができる。

3. 今のアーキテクチャのユニットテスト(単体テスト)はなぜ仕様書が必要ないか?

当然ながらテストの品質というのは大変重要だ。このレベルの単体テストに関してもそれはもちろん重要だ。しかし、この細かいこのレベルのテストに対して仕様書を書いたところで品質は向上しない。何故ならコードを書くものにとっても、事前にどういうクラスでプログラムを構成するか?というのは最初の時点でわかっていないからだ。

  現在の言語やアーキテクチャの状況だと、インターネットにある仕様書がいつもだたしいとは限らないのは皆さんもご経験済みだろう。ある時点で正しくても、バージョンが変化して挙動が変わるなんかしょっちゅうである。だから、スパイクと言って、本当にそれが動くのか実験しながらコードを書いていく。つまり、コードを書くものにとっても、書き始めた時点で明確にこういうコードになるというのは少なくとも100%想像がついていることは稀だ。今の複雑な仕様と、本当に動くかわからないAPIに対して、事前にテストを書くことは神業に等しい。プログラマは、大まかな設計はするかもしれないが、コードを試行錯誤し、すこしづつ進化させながらコードを書いていく。だから、マーチンファウラー氏曰くは「コードは設計作業であり、実装はコンパイラがやっている」と言われている。

コードですら予想がつかないもののインターフェイスと内部実装についてのテストを事前に書くことは相当難しく、事後に書いたとしたら、それはいったい誰がレビューするのだろうか?なぜなら、そのコードは、対象の言語仕様や文化、アーキテクチャ、最新のライブラリ状況に詳しい人しか良し悪しの判断ができない。

毎日コードを書いていない者にはそれは不可能だ。エバンジェリストである、筆者にも難しいかもしれない。それを考えると、対象の言語、コード、アーキテクチャ、ライブラリに詳しいものにとって、最も信頼できる仕様書のようなものは何だろう?

「コード」だ。「API仕様書」よりも、最も信頼できるものが「コード」だ。だから、今の開発プロセスでは、このレベルの単体テストは「テストコード」を書くようになっている。そういう人にとっては、これぐらい細かい内容だと、「コード」を
読むほうが確実なのだ。

 もちろん、コードのみでいいというつもりはない。それが一番望ましいとは思うが、「API仕様書」が意味があるように、細かいコードであっても、ある程度の「抽象化」は意味があるだろう。「API仕様書」はExcelで書いたりしない。いろんなオープンソースのプロジェクトを見ても明白だ。どうするかというと、コードと密接に関係しているので、大抵はコードのコメントで書いて、API仕様書を自動生成できるようにしている。もっともコードに近いので、最もメンテがやりやすい。だから、自動化されたユニットテストでは、API仕様書用のコメントの代わりのものとして、テストのメソッドがテストの概要を表しているような書き方になるようにチームで習慣づけるすると、最近の、自動ビルドの環境を使うとテストレポートが出てくるので、コードを読む前にも概要がわかりやすい。

RSpecを使ったテストコードを読もう  [倉貫義人、松村章弘,TIS株式会社/SonicGarden] より

しかし、ここまでしても、内容がコードとぶれる必要がある。だから、例えばRubyとかのAPI仕様書は、API仕様書から具体的なコードを読めるように工夫されている。

だから、このレベルのユニットテスト(単体テスト)を書く場合は、先に書く場合、あとに書く場合両方のケースにおいて「仕様書」と呼ばれるものは無駄なのだ。書いても形骸的なものになり、ユニットテストコードと同一になるので、全く
意味がないものになり、重複作業を生むだけになる。これは、工数の無駄だ。

4. 自動化されたユニットテストのメリット

 こうして書かれた、「ユニットテストコード」は何が嬉しいだろうか?まず、従来のレベル以上に、細かいテストを書くことになり、それが自動化されている。つまり、何回でも繰り返しテストをできることになる。つまり、何かがおかしくなっても、それを自動で流しておくと、いつでも問題に気づけることになるのだ。アジャイル開発などの繰り返し型のプロジェクトではこの技術は必須だ。繰り返し開発をして変化を受け入れるので、その際自動テストがないと、デグレードや、バグを新たに作りこんだ時に気づくことが出来ない。

 また、テスト駆動開発という自動テストを書きながらプロダクションコードを書いていく開発の設計・テスト作成の技術がある。そういうノウハウを使うことで、複雑なロジックでもテストを書きながら少しづつテストを書きながら開発をすることができる。複雑なロジックであっても複雑な問題を分割して、簡単にして考えることがしやすくなる。

[View:https://www.youtube.com/watch?v=7ttVXhUkN3c:0:0]

5. 品質よい自動化されたユニットテストを書く方法

 では、品質の良い自動化されたユニットテストを書く方法はどうすればいいだろうか?先ほど上げたテスト駆動開発を学ぶことは一つだが、それをもってしても妥当なテストケースを考えることができる能力が必要である。この分野のテスト技法を学ぶということもあるが、私のおすすめは「Pull Request」と「チームのレビュー」である。つまるところ、最新のアーキテクチャを持った言語に精通したプログラマ同士のレビューである。少なくとも、オブジェクト指向の原則やパターンとか、テスト駆動開発等をわかっている人物にレビューしてもらうのがいい。最初は時間がかかって無駄に見えるかもしれないが、レビューをすると人が育つので加速度的に、しょうもない指摘事項が少なくなっていく。

参考までに、著名なソニックガーデンさんがどのレベルでレビューをしているかがわかる好エントリがあるので、参考までに貼っておきます。

ソニックガーデンで行われているコードレビューの具体例をお見せします

 レビュー以外の方法で有効なものとしては、最近は「静的解析」や「自動コードレビュー」というものが有効だ。「チェックリスト」などの方式はコードの実装レベルの話なので、もし適切に書いたとしても、チェック項目が多くなりすぎて機能しない。そういう機械的にできることはツールに任せて自動化してしまおう。無料のツールや、簡単に導入できるサービスもあるので是非導入していただきたい。

自動コードレビューサービスCode climateでの解析例

6. 自動化されたユニットテストを管理する法

 あなたが、マネージャだとして、こうした自動テストを導入して、彼らを管理するにはどうすればいいだろうか?カンタンだ、管理するのをあきらめるとよいのだ。過激に聞こえるかもしれないが、今の時代は技術の流れが速すぎて、当のプログラマですら時代の流れについていくのは大変だ。そんな難しいことを普段コードを書く回数が圧倒的に少ない、もしくは全くしていない人がどうしたら把握できるだろうか?正直無理なのだ。
 であるので、最近のマネージメント方法としては、「サーバントリーダーシップ」と呼ばれる方法が推奨されている。端的にいうと、チームに任せて、彼らに外部から障害が降りかかった時にそれを助けてあげるというファシリテーターのような役割だ。今までは、マネージャがいろいろ指示していたかもしれないが、チームが自律的に考えて実行できるようになるように、手助けをする。そうすればチームは、徐々に自主的に判断する力がついてくる。細かいことを知っているので適切に技術的な判断ができるようになってくる。

 そういう時代になったので、次にどうするか?というと、チームが「適切に自動テストを書いているか?」ということをあなたがテスト駆動とテスト技法の専門家でもない限り知りようがない。ただし、チームがちゃんとテストコードのレビューをしているか?とか、テスト駆動とテスト技法に詳しい人をチームに入れてレビューしてもらうよう促進することはできるだろう。

あとは、カバレッジや静的解析、自動コードレビューは、プログラムに負荷がからないので、自動化して、プログラマが自分のコードの改善のガイドにしてもらうといいだろう。ここで例えば、ユニットテストを100%書くこととか、カバレッジを100%にすることとか、中身がわからないのに、数値だけですべてを判断しようとしないことだ。

 例えば、私はこんなプロジェクトを見たことがある。コードカバレッジが80%になるよう、自動化されたテストコードを書くように定められていた。そのプロジェクトは大変品質が悪かった。大きなプロジェクトだったが、何故問題があるのかわからないようだった。なぜなら管理者の誰もが実際に「テストコード」を読まなかったからだ。私がテストコードを実際に読んでみると、自動のユニットテストは大量に書かれていたが、assertと呼ばれる実際にテストを行うコードが一行も書かれていなかった。つまり、カバレッジを通すためだけに、プログラムを呼び出すためだけのプログラムがそこに書かれていたのだ。これは、工数の無駄以外の何物でもない。

 しかし、あなたがテスト駆動とテスト工学を学ぶのもいい手だが、恐らく一番簡単なのはそれが出来る人にレビューして貰うことだろう。自分でなんでもかんでもする必要はない。マネージャは仕事がいっぱいあるはずだ。チームが自主的にできることはやってもらおう。できないなら、少しづつできるようになってもらおう。

7. 自動化された適切なテストを書いて世界で勝とう!

先日、DevOps Enterprise 2015というサンフランシスコで開催されたイベントに行ってきた。驚いたことに、スタートアップの企業だけではなくエンタープライズの大手企業、それの生保や、銀行などの企業が、第二世代アジャイルと言われるDevOpsと呼ばれる考え方を実践していて、1日に10回以上もサービスをデプロイできるような開発チームを作り上げていた。それだけ見ると、米国人はとても優秀に見える。私は米国マイクロソフト所属で同僚は全員外国人だが、米国人を観察していて思ったが彼らは日本人と比べて優秀というわけではない。むしろ単体の能力でいうと日本人のほうが優れて見える。ずっとよく物を考えている人が多いのだ。

 しかし、彼らが素晴らしいところがあって、すぐやる、すぐ人に聞く、そして無駄なことをしないということだ。彼らがいつも言っていることは「最小の時間で最大のインパクトを」といつも言っている。残業して成果をだしても褒められたためしはない。もっと、短い時間で楽にインパクトを!という意味で「Be Lazy」と上司から言われるのだ。もっと休めと。そうやってみてだんだんわかってきたが日本人の心だと、一生懸命時間をかけてやろうとおもうと、毎回同じだけの時間がかかってしまう。ところが、「Be Lazy」を意識すると、同じ成果に対してだんだん短い時間でやれるようになってくるのだ。そうなると、余った時間で次のことをすることができる。そしてリラックスできる時間が持てる。これは簡単ではないが「一生懸命、頭をつかって怠け者になる」よう頑張るのだ。

 そういうちょっとしたことをすれば、日本人は世界的にユニークだし、能力は高いし真面目だ。圧倒的に勝てる。だから、日本の無駄は本当に無くすようなことに少しでもご協力できればと思う。もちろん私ももっと無駄をなくして、世界で勝てる人になりたいと思っています。

2016/1/25 追記

このポストを受けて、テストの真のプロフェッショナルと私がいつも尊敬している@kyon_mmさんがフォローブログを書いてくれましたのでご紹介させていただきます。ご本人から次のようなコメントをいただきましたが、その内容がフォローされている素晴らしいポストになっているのでご紹介させていただきます。大変勉強になりますので、是非ご一読ください。

  • これで単体テスト仕様書がいらない理由にはならないのでは?
  • テストレビューの成長方法について書かれていない(そうでないと、出来る人がいない現場はあきらめないといけないのでは?)

テスト(コード)レビューの方針 書きなぐり版 - うさぎ組