t-wada氏に聞く、テストを書き始めるための「はじめの一歩」

2024年7月19日

プログラマ、テスト駆動開発者

和田卓人

学生時代にソフトウェア工学を学び、オブジェクト指向分析/設計に傾倒。執筆活動や講演、ハンズオンイベントなどを通じてテスト駆動開発を広めようと努力している。『プログラマが知るべき97のこと』(オライリージャパン、2010)監修。『SQLアンチパターン』(オライリージャパン、2013)監訳。『テスト駆動開発』(オーム社、2017)翻訳。『事業をエンジニアリングする技術者たち』(ラムダノート、2022)編者。テストライブラリ power-assert-js 作者。

講演や執筆などを通じ、日本におけるテスト駆動開発のエバンジェリストとして知られる和田卓人さん。

TDDとは何かを改めて言語化してもらった前回の記事では、「テストを書かずに進むのが合理的といえるときはある。でも、後からテストを書くのって難しいしつらい」とのお話がありました。

テストが書かれないまま開発が進んだ状態から、テストを整備し、安心してコードをいじれるようになりたい。でも、既にテストのないコードが山積している中では、どうやってテストを書き始めればいいのでしょうか。TDDを実践するために必要な発想の転換やその効果とともに、詳しくお聞きしました。

テストの整備を進めるために、チームは何をすべきか

——テストが書かれないまま始まったプロダクト開発では、テストを書き始めるタイミングが失われがち、と前回お話されていました。 こういった開発現場では、まず何から手をつければよいのでしょうか。

和田:テストがないコード、つまり安心していじれないレガシーコードが山積した状態を改善しなくてはならないとき、トップダウンとボトムアップでそれぞれやるべきことがあります

まずトップダウンでは、経営者や意思決定層に、自動テストを書くためにコストを割く決断をしてもらわなくてはなりません。そのために彼らに、テックリードなど技術的な責任を負う人や、あるいは私が外部の人間として、自動テストを書く意味や戦略について話し、説得します。

▲和田さんは、経営者や意思決定層への説得の際に、この資料をよく使用しているそう

和田:ボトムアップ、つまり現場では、自動テストを書き始める前に、そのほかのあらゆる細々としたことの自動化から始めます

自動テストを書いたことがない人が多くいるチームで、自動テストを書き始めるには、まず自動テストの書き方を学ぶための時間が必要ですよね。学習時間を確保するために、まずはビルドや日々の書類作成などの手作業が減るような自動化を、チームで進めていきます。より多くの手作業が削減できるものから取り組んでいくとよいでしょう。これを根気よく続けていけば、少しずつ、学習に取り組む余裕が生まれます。

そうして余裕ができたら、自動テストの書き方を学びつつ、少しずつ、既存のコードに手を入れながらテストを書いていきます。テストを後から書くのはとても難しいことですが、書籍を参考に進めます。たとえば『レガシーコード改善ガイド』などで、必要なテクニックが解説されています。

書籍で学んだテクニックをもとにテストを書き、既存のコードを改善し続け、テストカバレッジが上がっていくと、動作確認の手間がどんどん減っていきます。人間の手と目で動作確認していたものが、テストを実行すれば自動で動作確認できるようになる。するとまた時間ができるから、その時間を使ってもうちょっと自動テストの範囲を広げる…。

こうしてじわじわオセロの盤面をひっくり返していくような、地道なアプローチをとっていきます。私の経験上、このような動き方を始めてから大体1カ月以内で、生産性に違いが現れてくるはずです

——地道に少しずつ進めていけばよいのですね。こうして自動テストの整備に注力し始めるのは、いつからが望ましいのでしょうか?

和田:タイミングの判断はやはり難しいですね。抽象的な言葉ですが、「いまよりもっと高速に開発を進められるようにならなくては」「もう一段アクセルを踏むぞ」と感じたときなのではないでしょうか。

テストを書かず、ただひたすら高速に開発し続けていると、いじれないコードが増えていき、開発速度はじわじわ遅くなっていく。このことが意思決定者に伝われば、自動テストの整備に投資するタイミングを見極められるだろうと思います。

TDDでコードを書くのは「ゴールから逆算している」わけではない

——レガシーコードにテストを書いていくときは、チームメンバーみんなにTDDを実践してもらうべきなのでしょうか。

和田:いえ、そうは思いませんよ。現に私は、こうした開発現場の支援に入った際、狭義のTDDは採用しません。TDDのかわりに、チームには「開発者テスト」を義務付け、コードに手を入れた日のうちにテストを書くようにしてもらいます。

TDDはプログラミング手法のうちのやり方の1つでしかなく、最終的に自動テストが残り、保守性を高められれば、どんなやり方でもいいと思うからです。

それに、TDDのやり方が合わない、と感じる人も一定数いるのです。

たとえば、設計段階ではなく、実装段階に入ってからどういうものをつくるのか具体化していきたいタイプの開発者は、TDDの「コードを書く前にテストを書く」というやり方が苦手だと感じがちです。というのも、「テストというものは、どういう状態になったらこの機能を完成とみなすかを明確に認識していないと書けない」と思い込んでいるからです。

たまに「和田さんは、コードを書く前から機能の完成形をちゃんと見通せているから、先にテストを書けるんですよね」と言われることがあるんですが、そうじゃないです。むしろその逆で、先々が見通せていないからこそテストを書くんです。

——完成形から逆算してテストを書いているわけじゃないんですか?

和田:はい。TDDを実践しながらコードを書く感覚をたとえるなら、不確実性にまみれたソフトウェア開発に少しずつ確実性を持たせていく、一種の陣取り合戦みたいなものです。

手を動かし始める前って、今から何をつくるべきかは漠然と決まっているものの、どのようにコードを書いていいかわからない「よくわからない領域」が目の前に広がっていますよね。その中で「こう動いてほしい」という期待を反映したテストを書き、それに対するコードを書いて、動かしてみる。テストが通れば、「よくわからない領域」の中に、「ここは分かった、自分が考えた通りだった」という領域を得られます。さらに、このとき書いた自動テストが通り続けていれば、「分かった領域」で居させ続けることができる。

テストを書きながら開発を進めることは、こうして「よくわからない領域」を少しずつ切り崩し、「分かった領域」を組み合わせながらシステムをつくっていく感覚なのです。

とはいえ、人によっては、コードは設計が完全に終わってから書き始めるものだと思っていたり、テストは自分の仕事ではなくテスターに任せるものだという思い込みがあったりします。そういった状態からTDDに慣れていくには、ある程度発想の転換は必要かなと思いますね。

「こうなったらうれしい」を分解すると、テストが書けるようになる

——テストファーストの発想に切り替える方法はありますか?

和田:TDDをやったことがない人にアドバイスする際には、「このコードを書くことで、使う人にどのような機能を提供できたらうれしい?」と、ユーザーの目線で考えるよう促します。それも「ワンクリックでものが買えればいい」といった大きい「うれしい」ではなく、もっと粒度の細かい「うれしい」をイメージしてもらうことからはじめます。

——具体的に教えてください。

和田:たとえば「レシートの画像をアップロードしたら自動的に家計簿がつけられるアプリをつくりたい」とします。

できるようになったらうれしいことをリストアップすると、おそらく「レシート画像から項目と数字を抽出してくれたらうれしい」「レシートをアップロードやダウンロードする機能ができたらうれしい」などが挙がると思います。でも、このレベルの「うれしい」ではまだ粗いです。TDDの「テストを書く→コードを書く」のステップを始めるには、自動テスト1つで動作確認できる大きさにまで、「うれしい」を分解していく必要があります。

先ほどの大きな「うれしい」をさらに分解して「軽減税率対象の商品とそうでない商品を判定できたらうれしい」「食料品と雑貨など項目ごとに分類できたらうれしい」など、より具体的に落とし込んでいきます。そうして挙げられた、自動テスト1つで動作確認できる小さな「うれしい」たちをまとめたものが「テストリスト」となるのです。その中から、テストが書けそうな項目をひとつ選んでもらい、実際にテストを書いてもらいます。

ちなみに、はじめの1つを選ぶときは、最初は「自分がテストを書けそうだと思うか」という感覚で選んでもらいます。慣れてくると、最初の1つをスッと選べるようになります。そして、この選びやすさがいわゆるテスト容易性であり、それは制御容易性とか観測容易性とかに分解できるというように、言語化ができるようになっていきますね。

——テストを書けるサイズになるまで、つくるべき機能に必要な要件を分解するんですね。

和田:はい。自動テストは、「この価格とこの価格を合算した結果、何円になったらOK」というように、曖昧さが入りこむ余地がないほど具体的にしか書けません。

この「具体的にしか書けない」という瞬間を積み重ねていけることが、設計力の向上にも大きく役立つんですよね。

——それはなぜですか?

和田:さっきのレシートのアプリをつくる例だと、「アプリをつくるぞ!」となった際、ほとんどの開発者は「レシートのスキャン機能」「ダウンロード機能」「アップロード機能」など、設計を大きめの粒度で考えてしまいがちです。「コードを書くぞ!」となってようやく、具体的に何をどう処理するのかという実装方法に頭が働くようになります。その時初めて「最初に思い描いていた設計では実装できない」と気づくから、設計からやり直すことになる。

一方、テストは具体的にしか書けないので、テストから先に書いていくと、早いタイミングで具体的に考えるよう強制されることになります。すると、テストを書く中で「この場合も考えて実装しなきゃダメかも」「今の設計じゃダメかも」と、実装や設計の足りないところにも気づけるようになる。特に、一番最初のテストを書くタイミングに、最も多くの気づきがあります。TDDではその美味しい瞬間を逃さないように、気づいたことをテストリストに転記して残しておくようにと指南されています。

だから、TDDに則って、「具体的にどうなったらうれしいのか」を分解し続け、突き詰めて考えていくことによって、設計する力も養うことができるのです。

優れた設計者ならあらかじめ気づけることでも、一般的な開発者には気づけないことはたくさんある。その中には、一般的な開発者でも、視点を変えれば気づけることもたくさんあります。TDDやテストファーストは、視点を変えることで、普段気づきにくい設計の粗に気づきやすくする仕組みであるとも言えますね。

▲取材はオンラインで行いました

TDDが難しく見えるのは、難易度が上がりきった状態ではじめようとするから

——TDDを実践することで、設計する力もついていくのですね。しかし巷では「テスト駆動開発は難しい」という声が少なくありません。それはなぜだと思いますか?

和田:テストがないコードが累積し「このままでは立ち行かない」となった段階で、TDDを採り入れようとするからです

テストがないコードに後からテストを書くこと自体がそもそも技術的に難しいので、ハードルが上がりきった段階でTDDをやろうとすると、難しさが際立って見える。ですからこの難しさは、TDDや自動テスト自体の難しさではなく、システムが抱える技術的負債の大きさに比例する難しさと捉えるべきだろうと思っています。

難易度は高いものの、既存のプロダクトが抱えるレガシーコードを改善するテクニックは存在します。もし開発プロセスに何らかの課題を感じるのであれば、ぜひ自動テストを書いていくことを検討してほしいですね。TDDの導入が難しかったとしても諦める必要はありません。私はTDDのなかの自動テストを採り入れるだけでも、よりプロダクトの成長を確かなものに変えてくれると考えています。

取材:武田敏則(グレタケ)
構成・編集:光松瞳、王雨舟

関連記事

人気記事

  • コピーしました

RSS
RSS