理由はそのうち考える

まずやってみよう

TDDBC(YouTubeLive)に参加してTDDへのモチベーションが爆上がりした

TDDBC

tddbc.connpass.com

8/1 10:00~12:00でTDDBCの基調講演+ライブコーディングがあった。自分のプロジェクトにもユニットテストはあるにはあるけどTDDとしてはやっておらず、以前より移行したいと思っていた。参加してとても勉強になったので、忘れないうちにまとめておく。

テスト駆動開発(TDD)とはなにか

TDDとはなにかを語る上で外せない本がある。それがテスト駆動開発である。Kent Beckが書いた唯一の本であり、TDDのバイブルとも呼べる本。持ってない人は購入して読むべし。ちなみに自分は積ん読になっているので早く読みます。

www.amazon.co.jp

TDDのゴール

その書籍テスト駆動開発のまえがきに以下の一節がある。これがTDDのゴールである。

「動作するきれいなコード」。Ron Jeffriesのこの簡潔な言葉が、テスト駆動開発(TDD)のゴールだ。動作するきれいなコードはあらゆる意味で価値がある。

テスト駆動開発』より引用

一部の天才を除き、一気に書くことはできない。
そのため"動作する""きれいな"コードに分割統治する必要がある。

TDDのサイクル

TDDは以下の手順を繰り返す。

f:id:aganik:20200802140043p:plain
TDDのサイクル(画像はTDDBCより)

ToDoリストで共有する

ToDoリストを作ってすることを共有する。このときのポイントは全てがテスト対象にはならないこと。重要なもの、或いはテスト容易性の高いものに絞って進めていく。特に一周目はテスト容易性の高いものから始める。理由はまったくゼロからのコーディングとなり設計の要素が強く、重いものになるからである。

TDDにおけるリファクタリング

リファクタリングとは一般に、"外部のふるまいを変えずにコードをきれいに書き換えること"と定義されるが、TDDにおいてKent Beckは冒頭の"外部のふるまいを変えずに"の箇所を"成功しているテストが成功しているままで"に置き換えた。これにより抽象的だった表現が具体的となった。なお一回のリファクタリングでかける時間はせいぜい5~10分くらいだとのこと。サイクルがぐるぐる回るため何度もチャンスがあるので、時間をかけすぎないことが重要。こだわりだすといくらでも時間をかけてしまうので、割り切りが大事。

ライブコーディング

FizzBuzz問題を例に取りライブコーディングが始まった。
ちなみにFizzBuzz問題は以下のようなもの。

1から100までの数をプリントするプログラムを書け。ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、3と5両方の倍数の場合には「FizzBuzz」とプリントすること。

問題を砕いていく

まずは以下のような感じで問題を砕いていく。 このタスクを分類するのはスキル。経験&理論で埋めていくとのこと。経験の部分は有識者からのフィードバック、理論は書籍などから。

  • 1から100まで(1からnまで)の数を文字列に変換する
  • プリントする"は容易性・重要度ともに低いので分ける
  • 3の倍数のときは数の代わりに「Fizz」と変換する
  • 5の倍数のときは数の代わりに「Buzz」と変換する
  • 3と5両方の倍数のときは数の代わりに「FizzBuzz」と変換する

優先度を分類する

テスト容易性と重要度の4象限で分ける。
ライブコーディングでは以下のように分けていた。

  • テスト高:重要度高
    • 数を文字列に変換する
    • 3の倍数のときは数の代わりに「Fizz」と変換する
    • 5の倍数のときは数の代わりに「Buzz」と変換する
    • 3と5両方の倍数のときは数の代わりに「FizzBuzz」と変換する
  • テスト低:重要度低
    • 1から100まで
    • 1からnまで
    • プリントする

例えば"プリントする"は、テスト容易性・重要度ともに低いため除外する。"1から100(n)まで"というのも、数を文字列に変換できればよいので除外する。

一回目に必ず失敗させる意図

明らかに失敗させて意図的に失敗したことを把握する。もしかするとその失敗は意図しないものかもしれない。敢えて意図的に失敗させることにより、スタートラインに立てていることを確認する。前提条件が狂っていて 時間を浪費しないようにするためである。こうした(散々調べ回ったのに結局は環境の問題だったみたいな)経験はまあよくあるので、このアプローチは素晴らしいと思った。ちなみにコンパイルエラーはRedなのかというと、Redとして扱う。失敗を予測・把握できているうちはRedとするとのこと。

ちょっと意外だったこと

日本語でテストを書くこと。書ける環境にあるなら母語で書いたほうが良いとのこと。テストコードは動作するドキュメントだからで、テストコードから仕様が読み取れるようになっていなければならない。

f:id:aganik:20200802142411p:plain
日本語でテストコードを書く(画像はTDDBCより)

3Aパターンでテストを書いていく

準備・実行・検証(Arrange/Act/Assert)でテストを書く。普通は準備から書こうとするが、TDDでは逆で検証から書いていく。準備や実行に時間をかけすぎると、何がしたかったかを忘れてしまう。またその時間に見合うだけの検証もいっぱい書いてしまう。ゴールに意識を集中するために先に検証から書いていく。

プロダクトコードを作る前に使う

作る前に使うことにより、使う側に意識を持っていける。作る側は作りやすい方向に舵を切ってしまう傾向がある。そのため、作りきった後に使いづらさに気づくことになる。作った後なので当然変えにくい。また使いにくさを丁寧なドキュメントで補おうとしてしまう。これは全員にとって悲しいことなので、使いやすさ読みやすさを先に考える。

テストを最速で成功させる意図

テストを最速で成功させることを"仮実装"と呼ぶ。プロダクトコード側を見ると途轍もなく単純なコードが書かれている。茶番のように思えるこの行為がなぜ行われるのかというと、テストコードが間違っていないことをまず確認するためである。テストコードもコードなので、バグがある可能性も当然ある。そのため、間違いようのないコードを先に書いてテストコード側の検証を行う。ははあと思わず唸ってしまった。素晴らしい。

1テスト1アサーション

One assertion per test と言われ、一つのテストに一つのアサーションがシンプルで望ましい。やたらアサーションを書かれているテストを見ることがあるが、どこかでコケると以降が全て止まってしまう。これはテストとしては後退しているため良くない状況である。

テストの構造化とリファクタリング

まずはじめにテストコードは最小限に留めること。三角測量で書いたコードは消しておく。なぜならテストコードは後になって消すことができないからである。正確にはその判断をすることが難しいから。またテストはメンテナンスコストが高い。理解容易性が低いテストコードは癌になる。さらに、テストを書いた本人がテストの仕様の構造や中身について記憶が残っているうちに、自分で構造化およびリファクタリングを行っておく。ここまでやってテストコードが動作するドキュメントととなり、役立つものとなる。

まとめ

以上のまとめ。

f:id:aganik:20200802143139p:plain
まとめ(画像はTDDBCより)

感想

すごく良かった(小並)。とても濃密な2時間であっという間に感じた。TDDに対する理解が深まり、モチベーションが上がった。自分のチームにも伝えてTDDを広めていきたい。あとライブコーディングではIDEをとても使いこなされているように見えたので、やはりツールの使い方を学び、洗練させていくのは必要だなと感じた。