コードは2回書きたい

TDD についておさらいしておきたいなと思ったので読んだ

t-wada.hatenablog.jp

とても良かった。自動テスト、テストファースト、テスト駆動開発のそれぞれについて、どういうものなのか・効果・注意点が分かりやすく説明されている。たしかに、自動テストは必ず使うけど、テストファーストやテスト駆動開発は状況に合わせてやったりやらなかったりする

書籍「テスト駆動開発」の付録Cと対になっているということなので、付録Cも読みたくなって読み直しておいた。そちらにはテスト駆動開発のこれまでとこれからについて書いてあるので、頭の整理ができてとてもよかった

Checking Driven Development

付録Cでは、開発者自身が書く自動テストはテストではなくてチェック、ということについて触れられている。そうだなぁって思う。自動テストでは、自分が考えたとおりに動くかどうかをチェックしている

そして、ソフトウェアデザインの記事では、テスト駆動開発の注意点として「全体の視点を失った、視野が狭い設計が残ってしまう可能性がある」という点が挙げられている。これもそうだなぁって思う

これらの記事を読んでみて、じゃあ自分はどういう風に開発を進めてるかなって考えてみると、テスト駆動開発はしてないんだけど、全体の視点に気をつけながらチェック駆動な開発をしてると言えるのかもしれないと思った

今の自分の進め方を、ちょっと書いてみようと思う

1回目はテストを書かずに進む

簡単な Web API の開発を考えてみる。その API を呼び出すと、セッションのチェックをしたり、DB とやり取りをしたり、他の API を呼び出したりして、結果を返すもの

こんな風に実装したら良さそうかなって、ある程度の道筋を頭の中に思い浮かべることができたら、細かくテストを書きながら進む前に、まずは、その道がゴールまでつながっているのかを確認したい

一歩ずつ足もとを固めながら進んでいったら、あと少しのところでゴールにつながってなかった!ってのは嫌なのだ。だから、まずは、雑に実装をしてゴールまで行けることを確認する

エンドツーエンドで確認

TODO リストは、こんな感じになってる。というよりクエスチョンリストというか

  • API のハンドリングは想像したとおりに動く?
  • セッションを保持できるよね?
  • DB につないで SQL を思ったとおりに実行できる?
  • 他の API を呼び出すのどんな感じ?

こういうのを、エンドツーエンドで確認したい。部品部品が動くことを確認していても、全部つないでみると、また別の課題が出てきたりするから、全部つないで「なるほどね」って言えたい

ので、適当に docker-compose ファイルを作って WireMock で呼び出し先 API のモックを用意したり、初期データが入った DB を立ち上げたりして、ローカル環境を準備する。そして、使おうと思っているフレームワークのハローワールドプロジェクトを立ち上げてコードを書いていく

一歩ずつチェックはしてる

この時点では、自動テストは書かない。でも、付録Cを読んで思ったのは、テストはしてないんだけど、一歩ずつチェックはしてるなぁってこと

全部書き終わって動かしてみて「あれ?動かない」じゃなくて、いっこずつ REPL で確認したり、デバッグ実行して確認したり、DB から取得した値をそのまま JSON で返して確認したり。そんな風にして「はい思ってた通りに動いたー!次いこ」だったり「あれ?思ってたのと違うなぁ。確認しよ」だったりチェックしてる

全部つないで確認してたら「あぁ、この組み合わせで起こる問題、全然想定してなかった!」みたいなのがあったりするので、そういうのは TODO リストに書き足す

また、途中で気になることがあったら横道にそれて、ライブラリの細かい挙動を確認したりもする。そんな風にして、不安をなくしていく

大きく変わることも多い

実際に動くものを作ってると色んな発見がある「ここは、別の方法でやった方が良さそうだなー」って思ったら、そっちに大きく舵を切ったりする

テストも何もないので来た道を気楽にサクサク戻って、汚いコードを撒き散らしつつ道を探す。そんな風に行ったり来たりしながら、全部つながって動くところまで確認する

本番レベルではないよ

注意点としては、この1回目のコードは、本番レベルのものではないということ。自動テストもないし、きれいでもないし、そもそも興味がない部分は書いてない。自分がもともと自信を持っている部分はほとんど書かないし、バリデーションも気になる部分だけしか確認しないし、エラーハンドリングも全部やるわけじゃない。

道筋が見えたらそれで OK のためのコード

全体を眺めてみる

そんな風にして、全部つながって動くところまで確認できたら、全体を眺めて、適当にレイアウトを考える

このパッケージにこのファイルを入れて、インターフェース要るかなぁ?んー考えすぎかな。必要になったら抽出するんでいいか。どういう単位でメソッドを切ったらテストしやすそうかなぁ、こういう感じかなぁ

とか考えながら、移動したり、まとめたり、切り離したりして、置いて眺めてみる。頭の中では良さそうに思えたものが、実際に置いてみると「なんか違う」ってなること多い。もっと経験を積んで、この辺の判断をサクッとできるようになりたいなぁ

この道は、ゴールまでつながってる、と自信を持って言えて、だいたいこんな風に進めば良さそうだな、ってなったら1回目終わり

このコードは参考にはするけど、実際のコーディングには使わない。最後は捨てる

2回目はテストを書きながら進む

1回目で道が見えてるので、あとはそれを丁寧に実装していくだけ。これはこれで楽しい

テストを先に書くときもあるし、実装を先にかいてからそのすぐ後にテストを書くこともある

ただ、後からまとめて書こうとするとめんどくさくなって雑になってしまう人なので、できるだけ同時にテストを書いていく

まずは失敗させる

テストを書くときは、先に書くときでも後で書くときでも、まずは失敗させる。失敗するはずのテストが成功してしまったら、そのテストはおかしい

テストが正しく動作することをチェックするために、まずはレッドにして、それからグリーンにするようにしている

プライベート関数に対するテストも書く

プライベート関数に対するテストも気にせずに書く。自分の思ってる通りに動いていることをチェックしながら進みたい

ある程度実装が進むと、プライベート関数に対するテストが、パブリック関数に対するテストでカバーされることが多いので、そうなったらプライベート関数に対するテストは削除してしまう。でも、パブリック関数に対するテストだけだと細かいケースが確認しきれなくて不安だなってときは、その不安を解消できる分だけ残す

もっと良い実装を思いつくことがある

1回書いているとはいえ、本実装を進めていくうちに、もっと良い実装を思いつくことがあるので、そういうときはテストを書きながらそっちに進んでいく

あれ?この場合どうなる?

テストを書いてると「あれ?この場合どうなる?」ってなることがある。そういうの好き

こんなところかな

この記事を書きながら、1回目のはコーディングというより、フィージビリティチェックだな。でも、それなしに頭の中だけで設計して、さぁぶっつけ本番でコードを書くぞー!ってのは、苦手。行ったり来たりしたい。そんである程度全体像が見えてから、自動テストでチェックしつつ前に進むのが好き

自分が前に進むときには TODO リストがあって、テストを書いていない場合でもそれをチェックしながら、小さい単位で進んでるんだなぁって確認できて面白かった

0回目

実際は1回目の前に、もういっこ作ってたりもする

それは、僕の思ってるものが、本当にユーザーの悩みを解決するのか?というのを知りたいから。プロトタイプをさくっと作って触ってもらって「こういうのでいいの?」ってチェックするときもある

おしまい