複雑なロジックの実装はTDDでやると良さそう

tl;dr

実装が大変な複雑なロジックを書く際に、テストを書きながら実装する(TDD をやる)と、以下のメリットがある。

  • テストを先に書くことを意識することで、タスク細分化の思考を強制できる
  • 段階的に実装することで、不具合修正のオーバーヘッドを最小限にとどめられる
  • 実装が終わる頃にはテストが書き終わっている

TDD やっていますか?

テスト実装のタスクは後回しになりがちである。
それは実装が最重要タスクで、テストは「時間があればやる」認識にどうしてもなってしまうから。
その「最重要タスクである実装」を、より早く、正確に、思考の補助までしてくれるのが TDD ということを認識することができれば、Test Drive な開発の一歩となるはず。
TDD の恩恵を実感する手段として、複雑なロジックの実装に取り組んでみるのが良いかもしれないと思ったので、メモとして残しておく。

複雑なロジックを実装する際に TDD でやってみる

例えば一つの関数の中に、A -> B -> C といった順で実行される処理があったとする。
それを以下の手順でテストを書きながら実装する。

  • A の処理まで実装する
  • A の処理が正常に動いているかどうかを判定するテストを書く
  • テストを実行し、問題ないなら次に進む。テストが落ちたら、テストが動くよう修正する
  • B の処理まで実装する
  • 以下、C まで繰り返し。

何が嬉しいのか

難しいロジックを書く際の思考整理

段階的にテストを書いていくことを意識すると、自ずとタスクを細分化する方向で思考できる。
複雑なロジックを考える際にやってしまいがちなこととして、考慮するポイントが多くて思考のメモリが足りなくなることがあるかと思う。
まずはタスクを細分化し、一つのタスクを達成するためのテストを書き、そのテストが通るよう実装する。TDD の考え方はこれを自然と強制してくれる。

不具合修正のオーバヘッドの短縮

全ての処理を一気に書き切ってから動作確認を行うと、複雑で長い処理になればなるほど、問題の特定が難しくなる。
例えば A ~ E の細かいタスク処理がある関数を実装した際に不具合が起きたとして、どの処理付近で想定外の動作を起こしているか、原因を特定するために console.log を置く作業が始まることは想像に難くない。
これに対し TDD で段階的に処理を実装していると、処理 A のテストが落ちると原因は処理 A にあるし、処理 B のテストが落ちると原因は処理 B にある(処理 A のテストは実施済みのため)。
テストを書きながら段階的に実装することで、「こう動くであろう」の考えで実装を進めず、確かな実装の進捗を感じさせてくれる補助輪的な役割も果たしてくれる。

テストコードの完成

実装が完了すると同時に、テストも書き終わっている。このテストは、今後の新規機能開発や改修において、リグレッションテストとして予期せぬ不具合の発生を知らせることに役立ってくれる。