テスト苦労開発、あるいはTDDの夢

やっとむ氏:みなさん、こんばんは。本日は「Tech-on MeetUp #03」にお越しいただきありがとうございます。

私は安井力と申します。

「やすいつとむ」と書いて「やっとむ」という名前で活動してるので、「やっとむ」という名前で検索していただいたら、なにか引っかかるかなと思います。

いろいろ書いてるんですけど、いまプログラマーはほとんどやっていなくて、アジャイルコーチ、スクラムコーチということをフリーランスでやっています。いろいろなお客さん、いろいろな開発現場に行って、アジャイル、スクラム、あるいは、テスト駆動開発を導入するお手伝いをしています。

最初に軽く宣伝させてください。ここにもあるように、来週の土曜日に「Coderetreat in 秋葉原」というイベントがあります(注:すでに終了)。

クリエーションラインさんをお借りして、普段仕事でプログラムを書いてる人で、プログラムが好きで仕事をやってる人が、仕事を離れてプログラムを好きに書けるとしたらどんなふうに書けるだろうという体験ができるイベントなので、よかったら来てください。まだお申込み若干名空いております。

これは私の本の宣伝、まあ、いいや。はい。

あ、まだ宣伝だ。

TDD実践講座。宣伝なんですけど、私がどういう人かという補足になってて、アジャイルコーチ、スクラムコーチをやっていて、もともとはプログラマー上がりです。

まさにケント・ベックの本『テスト駆動開発』が出た2003年ごろからやっていて、「これはすごい」「人生が変わった」と思って、ずっと勉強もしてるし、本を読んだりもしてるし、できるだけ広めたり、人に教えたりということもしています。

テスト駆動開発

なので、本日はテスト駆動開発について中心にお話をしようと思って来たのですが、もともとこのお話をいただいたときに「アジャイルの教科書的な話じゃなくて、生々しい話とか、苦労するような話をしてください」って言われたような気がしてたんですね。

それで蓋を開けてみると「アジャイル開発にすぐ使えるレシピ」みたいなイベントの説明になっていたので、なんか本来のイベントの趣旨と違う話になっちゃってるかもしれません。便利なレシピというよりはごった煮的な、あるいは、もしかするとちょっと魔女のシチューみたいな感じのところがあるかもしれませんが、そこらへんは楽しんでいただければと思います。

テストカバレッジ100パーセントとはどんな状態か?

私は、先ほど言ったようにいろんな現場でアジャイルコーチのお手伝いをしてるんですけど、これは5、6年前、とあるITサービス企業のお手伝いをしたときの話です。

すでに稼働しているサービスをいくつか持っている部や組織があります。基本的には開発組織なんだけど、古いサービスはほぼメンテナンスだけ、運用だけしている部分もあれば、新しめのサービスは機能追加もしてるような、けっこう歴史のある部署です。サービスもいろんな歴史のあるものがいっぱいあり、それぞれを2人、多くて4、5人っていうチームで、そういうチームがいっぱいある部署でした。

そこでえらい人が、「今期の部署目標は、自動テストカバレッジを100パーセントします」と宣言しました。ちなみにフィクションです。「リアルっぽい話をしてるな」と思ってるかもしれないですけど、フィクションだと思ってください。

さて、自動テストカバレッジ100パーセントって、どういうことですかね? オレンジジュース100パーセントと自動テストカバレッジ100パーセントの違いは、何だと思いますか?

A「本質的には同じである」。B「オレンジジュースは甘いがテスト自動化は苦い」。C「その他」。Aだと思う人。

(会場挙手)

Bだと思う人。

(会場挙手)

Cだと思う人。

(会場挙手)

ほとんどみんなBですね。AとCが若干名いましたが。私の説明、私の理解によると、正解はCです。

どういうことかというと、オレンジジュース100パーセントっていうのは、買う人にとって、飲む人にとってうれしいことなんですね。「オレンジジュース100パーセントだから買おう」「濃縮還元だから買おう」とか、あるいは、高級なやつになると原液そのままみたいな、絞ったやつをそのまま飲めるやつ、薄めてない感じですね、濃縮してない100パーセント。

一方、テストカバレッジ100パーセントっていうのは、ユーザー、お客さんには関係なくって、あくまで開発する自分たちにとって価値があることです。逆に言うと、開発側に価値がないんだったら、テストカバレッジとか考えてもしょうがないわけです。

カバレッジ100パーセントの定義

もう1つおもしろいのは、基準がどういうふうに設定されるかですね。

オレンジジュース100パーセントっていうのは、詳しく知らないですけど、たぶんどういうふうに作って、どういう条件があってっていうのが決まってるはずです。それを満たしてないものを「100パーセント」って言うと、どっかの役所から怒られるんだと思います。

一方で、自動テストカバレッジ100パーセントっていうのは、上司が言ったかもしれないけど、別に上司は100パーセントの定義なんて考えてなくて、あくまで開発チームが自分たちの状況に合わせて、自分たちの都合のいいように定義できるのが、「自動テストカバレッジ100パーセント」ですね。

測定方法と実現方法があるんだけど、それは自分たちで決めていい。これ、オレンジジュースの場合と大きく違う話です。

実は、自動テストがほとんど進んでいない部署でした。なので、この上司の人と我々コーチは、協力しながらこれを進めていたんですけど、「自動テストをちょっとでも進めたい。いまないんだから、とりあえずなにか書き始めれば、状況がこれ以上悪くなることはない。少しは良くなっていくだろう。良くなるきっかけになるだろう」という思いがありました。なので、どんな自動テストでもいい。

そこで取ったのが、RailsとかJava(といった)わりとモダンな言語やフレームワークを使ってるところは「じゃあ、単体テストはJUnit、RSpecで書いていけばいいね」と。

そうじゃなくて、もう「どレガシー」っていうか、CGIみたいなPHP、フレームワークとかを使ってない、昭和のPHPみたいなやつを使ってるところは、とてもユニットテストを書く余地がないので、もうブラウザからSelenium……、確かこのときSelenium IDEを使ったと思うんですけど。「機能テストを書いて、機能レベルでカバレッジを取っていきましょう」っていうアプローチをとりました。

要は、サービスによって、ないしは、言語とか、使っているテクノロジーによって、カバレッジの定義を変えました。自分たちの都合のいいように解釈しました。

テスト自動化はやりやすいところから

その結果、上司の人は「しめしめ」と思うわけです。テストがぜんぜんないところから、「テスト、なんでもいいから書け」みたいなことを言ったら、みんながテストを書き始めるというのができた。

最初のテスト自動化をするときは、まずはやりやすいところからやりましょう。ぜんぜんないのであれば、なんでもいいから、本当に書きやすいところっていうか、書けるところから書き始める。開発者がテストをするっていう習慣、プロセス、文化を作る、そこにないものをまず生み出す、ということができるようになります。

それから、環境ですね。最初はテストを走らせる環境もないはずなので、それも作っていくっていう動きになります。

あくまでこれは最初の1歩であって、このレベルで100パーセントを目指すのは、あんまり筋は良くないんじゃないかなと思います。っていうか、なんでも100パーセントを目指すって良くないですね。コードカバレッジだったら、70~80パーセントぐらいがだいたいいいところだけど、機能カバレッジだともうちょっと高くていいと思います。

あとは、また別の話ですが、上司の言うことは都合よく聞きましょう。そして上司の側は、都合よく聞かれたときにいい感じになるようなことを、フワッと言っとけばいいんじゃないかなと思います。

開発者は自分のためにテストを書くべし

また別の話。私、さっきも言いましたが、昔はプログラマーで、それこそJava 1.1~1.2ぐらいのころからプログラマーをやっていました。Railsは2ぐらいまでやっていました。

フィクションですよ? 金融系の開発で、いろんな会社から、下請け、孫請けが集まってきて、30人40人とかプログラマーがいて。そこでテスト期間がもうすぐ終わるっていうところで、バグがいっぱい出てるのをどんどんどんどん修正していくっていう期間でした。三軒茶屋の現場だった……、あ、フィクションですよ?

(会場笑)

もう締め切り間際、前夜だったかどうかは覚えてないんですけど、締め切り間際です。終電も近いっていうときに、マネージャーの人が私のところに来て、「これ直してください!」って言ってきました。私の出したバグだったかどうか覚えてないんですけど、というかたぶんわかんなかったんですけど、「直してください」と。終電もう近いです。みなさんだったらどうしますか?

まあ、さすがに「イヤです。帰ります」とは言わないですよね。「直します。直しました。できました!」って言います。さて、マネージャーの人は「テストチームが確認し終わるまで待ってください!」。

さて、終電は迫っています。みなさん、どうしますか?

私がどうしたかは、ご想像に任せるとして……。ここでのポイントは、私は、自分の書いたコードにそこそこ自信があったんです。でも、書いた人が自信があるかどうかは関係なくて、テストチームがあって、そこでテストするんだったら、テストが終わらなきゃ 、直ったかどうかは確認できてないわけです。「できてるはずだ」って祈ってても、意味がないわけですね。

じゃあ、プログラマーにとって、テストって何でしょう? 開発者は自分自身のためにテストをする。責任をもって、あるいは、自信をもって、「はい、できました」って言うためには、自分でテストをしてないといけない。これ、唯一の方法です。

もちろん他に支援する方法もある。レビューするっていうこともあるし、コーディングガイドに従うっていうこともあるでしょう。でも、テストは必要。

逆に、自分が死なないために、テストを自動化するわけです。マネージャーの人に「できましたか?」って言われたときに、「はい、テストしました」「え、ここもテストしましたか? あそこもテストしましたか?」っていうときに、全部テストしてたら、責任を取りきるのは大事だけど、責任を取りながら「うぅ……」って死んじゃったら、つらいわけです。

なので、自分が書いたものはテストが自動化されていて、ただ単にテストをするんではなくて、自動化してあって、すぐに、いつでも「間違いなくできてる」と自信をもって言えること、その状況が、1人でプログラマーをしているっていうコンテキストだと、大事になってきます。

チームが楽しく働くためにテストを書く

一方、チームだともうちょっと話は変わってきて。開発チームでは、チームが楽しく、あるいは幸せに働くためにテストを書きます。全員がお互いに責任をもつ。私が書いたコードはちゃんとテストしたし、あなたが書いたコードもちゃんとテストしてるんだから、少なくとも単体レベルのバグが出ても、人に迷惑をかけるっていうことはないよね。

こうなるとレビューするのはバグを見つけるためじゃなくて、改善のポイントを探すとか、あるいは、良いプラクティスを広めるためにやるんだよね。人の尻拭いなんかしてるチームで働きたくないです。逆に、人に尻を拭いてもらいたくもないです。

チームの外に対して「できました」という自信をもつこと、それはイコール、お客さまに対しても、あるいは、ユーザーの人に対しても、価値を届けられると自信をもてることにつながります。だから、テストを書きましょう。(いや、)「書きましょう」じゃないですね。テストを書いてない人は、おむつを替えてもらってるのと同じだと思ってます。

テストがないコードはレガシーコード

次の話。これまた数年前の話です。無事ローンチを迎えた新規サービスでの話。リリース前はこんな感じでした。PO、マネージャーの人が「ぜひリリースも間に合わせたいし、機能もできるだけ盛り込みたいから、がんばってほしい」。開発の人が「がんばります! がんばるんだけど、テストはちゃんとするけど、自動化してる余裕はないですね……」と。

リリースが無事にできて、「やったー! おつかれさまでした! おめでとうございます!」。さて、リリースが終わるとどうなるか?

「ユーザーがっつり獲得できてるから、これ、ガンガン利便性上げていくためにエンハンスしたいです。機能追加したいです。バグも深刻なやつ、重大な影響あるやつは、どんどん直していきましょう」。開発チーム、「わかりました、やりましょう! やっぱり自動化する余裕はないですね……」。

あ、これもフィクションですね。

ちなみにあれですかね、「こんな現場に自分はいないよ。自分はちゃんと余裕もあるし、テストも自動化できてる。そもそも当たり前だよ」っていう人、どのぐらいいますか?

(会場挙手)

あ、たくさんいますね。はい。

じゃあ、どうなるか? それでもテストは自動化したいわけです。

テストがないがゆえに、どんどんレガシー化していきます。レガシー化していくっていうのは、「テストがないコードはレガシーコード」という本、『レガシーコード改善ガイド』があります。

レガシーコード改善ガイド (Object Oriented SELECTION)

人の入れ替わりも多いので、「それを作った人、誰だっけ?」「もういないです」「この仕様なんだっけ?」「もうわかる人がいないです」ってどんどんなっていきます。

機能追加で仕様がごちゃごちゃしてきます。最初のリリースするときにはしっかり全部テストしてるから、手でテストするのが当たり前で、ちゃんとやるんですけど、ちょこちょこリリースが始まると、毎回毎回手動でテストしてるわけにはいかなくなります。手間暇もかかるし。

それから、現場のエンジニアの人が、「テスト駆動開発」っていうものにあこがれているっていう面もありました。私はここは、スクラムコーチないしはスクラムマスターでお手伝いしてたと思いますね。

テストを自動化し始めて起こったこと

現場のプログラマーの人たちが思い立って、数名が「自動化をしよう。まずはテストの自動化をできるようにして、そこからテスト駆動開発ができるように持っていこう」っていうことで始まりました。

メリットとコストをPOにも説明して、最初の自動テストはさっき言ったようにやりやすいところから、単純なロジックのテストから始めていきます。

最初の1歩はいいんですけど、その後がどんどん大変になってきて。CIがあっても、テストを実行する環境がそもそもないから叩けないっていうこともあるし、データベースが絡むとテストが面倒くさくなるし。

それから、ログインパズルっていうか、他システムに連携する、ログインするときに、たぶん会社のルールかなにかで「必ずパズルを表示するシステムを使ってログインすること」とか。あとは、会社のポイントシステムみたいなのがあって、それに連携しなきゃいけないとか。あと、ユーザー認証の話とか絡んでいて、そのあたりとテストを自動化していくのがすごく大変だと。

そして、力尽きて脱落していく人も出てくる。最初の一塊のテストを動くようにするのに、こういう苦労をけっこう何ヶ月かしてたと思います。でも、最終的には少しずつテストは増えてくるし、さらに、周りのプログラマーのチーム全体を巻き込んで、「テスト書けるよね」「書いていきたいね」っていう雰囲気になってきました。

テストは書けるところから

最初からテストを書くのは大変なんです。でも、後から書くのは、もっとずっと大変なんです。

大変なのは、自動テストを書くだけじゃなくて、そのための環境の整備から始まるわけですね。まずは「テスティングフレームワークは何を使うんだっけ?」「開発環境はどういうふうにして、各自の環境でテストを実行できるようにするにはどうしたらいいんだっけ?」で、もちろんCIまわりの話もありますし、その結果のレポーティングをどうするかっていう話もあります。

でも、開発者が「自分のためにテストをする」っていうことを判断する。自分のためっていうことは、人のためだったらお金もらってやるんですけど、自分のためだったら、やるかどうか判断しなきゃいけないんです。ROIを考えなきゃいけないです。

よく言うんですけど、テスト駆動開発とかテスト自動化をするときに、「いま何を作っていけばいいんだかわからない。手探りだ」っていうときには、あんまり効果がありません。なぜなら、1回テストを自動化するのにはけっこう手間がかかる。それを何回も何回も実行するからペイするんですけど、1回実行して、機能が、仕様が変わっちゃったらテスト作り直しになるので、資産としての価値があんまりありません。

なので、「ここは安定している。ここは安定してない。だから、安定してるほうはテストを書こう。あるいは、テスト駆動開発でやろう。こっちは別の方法でやろう」っていう考え方も必要になってきます。

最初にどこからテストを書くかっていうのは、まずはどこでもいいから書けるところからなんだけど、その後はレイヤーをつらぬくテスト、いわゆる曳光弾開発と同じ発想ですね、アーキテクチャをつらぬく……横かな、縦かな。

さらに、正常パターンをエンドツーエンドで、例えばログインして、検索して、カゴに入れて、オーダーして、決済する、みたいなところが1本だけでも自動化されていると、その後テストを継ぎ足していくのは簡単になります。

こんなアーキテクチャのレイヤーがあったとしたら、まずは1ヶ所だけ回すところから。そこから、例えばデータベースまでいくところを書き、さらにAPI経由で叩くところ、あるいはJSからAPIを叩くんだけどAPI側がモックになってるとか。

簡単なシンプルな値を書いたときになってるところ、こういうパーツが別々に書けるようになると、全体を通してのエンドツーエンドが書けます。あくまで箱のラベルは例で、これは一般論的な考え方になります。

いつテストを自動化するか?

エンドツーエンドで書けるようになると、さっき言った1つはハッピーパスを書く、正常系の一番典型的なパターンを1本書くことができます。

他には、リスクのあるところにテストを書く。お金まわりであるとか、ユーザー獲得に力を入れてるところだったら、そこで脱落するのを防ぐためにテストで担保するとか。

「複雑なロジック」というのをやってたのはAI的なやつですね。AI的なやつが論文どおりに実装できてるかを、絶対的には書けないんだけど、確率的な感じでなんとかテストを書こうということをしたり。

「なんでも全部テストを書こう」じゃなくて、プロダクトのリスクが大きいところに書く、これもやっぱり、テストの資産価値を高めるアプローチです。

そうやってテストの自動化はしたほうがいいし、どこからやっていくか、そして、いつ自動化するかという話もあります。できるなら最初からがいいんだけど、後からやるんだったら、テストができるかどうか、テストがしやすいような設計を入れておくことが必要です。

でも、それを管理するのが難しいなら、「最初からやれよ」って言いたいんだけど言えない場合もあるので、小さくてもいいから早期にちょっとだけ書いて、それを維持しておくと後々テストを書くときに「うわ、絶対これテスト書けねーや」みたいになることは最低限防げると思います。

よく、忍者が修行するのに、最初小さい苗を植えてジャンプしていく。だんだん伸びてくるのに合わせてそれを飛び越えるようにしていくと、いつの間にかスーパージャンプできる、みたいなのがあると思うんですけど。

この「開発チームが成長する」っていうのも同じで、実装、開発のところだけやっていて、テストのところを超えてないと、いつか大きくなってからテストをやろうとするとコケるわけです。つらいわけです。

だから、理想的には最初から、実装も、テストも、あるいは、システム自体が小さい単純なときから、一緒にテストも書いて、チームの力をつけながら一緒に成長していくっていう考え方が大事です。

どこまでテストを書くかは自分たちで決めること

ユーザー獲得に苦しんでるサービスがあって、「いや、僕らはものすごい品質とかテスト自動化とか大好きなんで、しっかりやります!」って言ったら、プロダクトオーナーに「もうちょっと速くならないですかね?」って言われたりっていうこともありました。

求められる内部品質、つまり「どこまでテストを書くか、どのぐらい書くか」というのは自分たちで決めることです。自分たちで決めて、さっきROIのところでもお話しましたけど、テストはコストがかかります。へたなテストを書くと、それは負債になります。テストも技術的負債になります。

テストの資産価値を高めるため、いまのプロダクトに必要なのは何なのか、SLAをベースに考えるのが一般的に良いと思います。エンジニアとして「ここまでやるべきだ!」っていう気持ちは、それはそれで大事なんですけど、いまプロダクトに求められてるのは何なのかっていうこともちゃんと考慮する。そこまで考えるっていうことは、プロダクト全体の品質を考慮したうえで、プログラマーが自分たちで品質基準を決めて、そこでテストを自動化していくことです。

アジャイルにおけるテスト駆動開発、ないしは、テストをどんなふうに自動化していくのかということについて、いろいろバラバラとお話させてもらいました。役に立つところがあればうれしいです。この資料はすぐに公開します。この後に何ページかあって、説明しなかったけど、実は「よくある話だよね」みたいなのも混ざってるので、それも含めてご覧いただければと思います。

じゃあ、まず1番手の前座はここまでにしたいと思います。このへんから楽しいお話につながると思います。ありがとうございました。

(会場拍手)