読者です 読者をやめる 読者になる 読者になる

「実践テスト駆動開発」 まとめその一

まとめていったほうが頭に入る気がするのでこれからこういう本を読むときは内容をまとめていこうと思う。

テスト駆動開発をすればオブジェクト指向ソフトウェアが正しく「育つ」

コードより「先にテストを書く」
→テストを先に書くことで、自分が何をしたいかとかを明確にしないうちに作業に着手することはなくなる。→より計画的に。
テストがコードを「導く」感覚。
なんかモックオブジェクトというものが大事らしい。これは、あるオブジェクトが関わっているオブジェクトとどのようにやりとりするかをテストするオブジェクトらしい。

Ⅰ部

第一章 テスト駆動開発のポイントとは?

デプロイ→フィードバックで一区切り。
変化に備えるために…①常にテストをてエラーを検出する。いっぱいすることになるから自動化しないと現実的じゃないよね。
          ②常にコードをリファクタリングする。コードをシンプルに。
問題:テストだるそう、面白くなさそう…
→TDD(Test-Driven-Development)によって打破。書き終わったコードを検証するテストを書くのではなく、先にテストを書く。それで何をしたいかを明確にする。

基本的なTDDサイクル
テストを通るようにする→リファクタリング→失敗するユニットテストを書く→テストを通る(ry のループ。

テストを「書く」ことによって得られるもの
・何をしたらいいのかが明確になる。
疎結合(多分モジュール化が進んでることだと思う)なものを作るようになる(独立してテストしやすいし、組み合わせも考えやすい)
・テストそのものがドキュメントになる
・網羅的なリグレッションテストスイート(?)を厚くできる

テストを「実行」することによって得られるもの
・まだ文脈が頭に残ってるうちにエラーが検出できる
・作業完了地点が明確になる(無駄に将来に備えた柔軟性とか機能を付けないようになる)

失敗するテストを書く、とは
まず、受け入れテストというものを書くところから始まる。これは構築したい機能を実効するテストだ。これが失敗してる間はまだ機能が実装されてないということだ。
この受け入れテストを通すまでの間にユニットテストユニットテストクリア、リファクタリングの小さいサイクルを回す。
ユニットテストは開発進捗レベル、受け入れテストは実働レベルのテストである。ユニットテストをクリアしてないコードをくれぐれもコミットしないように。

エンドツーエンドでテストする
内部コードを直接呼ぶテストは信頼性が微妙なのでなるべく外側からのインターフェースを通じたりしてテストしよう。

テストのレベル
受け入れテスト:システム全体が動くか?
インテグレーションテスト:変更不可能なコード(たとえば外部サービスとか?)に対して正しく動くか?
ユニットテスト:オブジェクトは正しく振舞っているか?扱いやすいか?

第二章 オブジェクトをテスト駆動開発する

オブジェクトが互いにどう作用しあうかを考えよう。
命じよ、訪ねるな
オブジェクトが意思決定をする場合、自分が持ってる情報か、トリガーとなるメッセージとともに渡される情報だけを下にすること。他のオブジェクトを操って云々、というのは避けること。
→設計柔軟性のため。こうしないと列車事故みたいなコードになる。
あと、何のために存在しているか明確に命名しなければならなくなるというメリットもある。中の情報が隠蔽される分、名前で振るまいを語らなければならない。
たまに訊ねるのも仕方ない
全部を「命じる」のもやはり難しいからやっぱり訪ねければならないときもある。そんなときでも気をつけることはある。
鋭い質問をしよう。本当に必要な情報が得られるような質問を。
まぁむやみに問い合わせはしないように。情報の隠蔽性が低くなるし。
このへんをどうやってテストする?
モックオブジェクトというものを使う。これを使えば、隣接オブジェクトとどのようにコミュニケーションするかを定義できる。こうした定義をエクスペークションと呼ぶ。
要するに周りのオブジェクトをこれで代用する。別に、まだ(周りの)本物が出来てなくても大丈夫。これで役割が明らかになる。
まとめると、モックを使ったテストの記述は、以下のようになる。

・テストに必要なモックオブジェクトを生成。
・テスト対象オブジェクトを含む、実際のオブジェクトを生成。
モックオブジェクトに、テスト対象オブジェクトからどう呼び出されるかのエクスペークション(前述)を定義する。
・テスト対象オブジェクトのメソッドを呼び出す。
・結果として出てきた値とか、ちゃんと呼び出しが行われたかをアサートする。

これがユニットテストの単位だ。ユニットテストは全てのオブジェクトをひとまとまりに生成し、テスト対象オブジェクトをとコラボレーターとの相互作用についてアサートする。

第三章 テストフレームワークの紹介

JAVAなので割愛。Rubyにもいろいろあるみたいなのであとで調べてみよう。

Ⅱ部

第四章 テスト駆動のサイクルに火を入れる

TDDでは基本的に新しい機能を加えるときには既存の基盤に対して新しい機能用のテストを差し込めばいいと言った。
→じゃあ最初はどうすんの
最初は結構だるい。何故ならビルドやデプロイ、テストサイクルなどをすべて自動化しておかないといけないからだ。
最初頑張っといたら後が楽だから頑張っておくれ

まずは動くスケルトンをテストする
最初の受け入れテストを書いて通るまではだるい。テスト用のツールとテスト対象の機能を両方作らなきゃいけないからだ。何か失敗してもどっちが、そこがおかしいのかわかりにくいしだるい。
解決法として、問題を二つに分割するというのが挙げられる。
動くスケルトンとは、実際の機能を可能な限り薄くスライスしたものだ。これに対してビルド、デプロイ、エンドツーエンドのテストを自動化しなければならない。
動くスケルトンには必要最小限の自動化、コンポーネント、通信システムを含めなければいけない。できるだけシンプルにわかりやすく。
たとえば、バックエンドでデータベースをなんかやるwebアプリならまずあっさりとしたページにDBから取得したフィードを表示するとかそんな感じ。
エンドツーエンドの「エンド」がシステムだけでなくプロセスも含んでいることにも注意。擬似本番環境みたいなレベルにしたい。
理由はふたつ。一つは、デプロイはエラーが起きやすいので自動化すべきだということだ。第二はデプロイは開発チームが仕様の確認をほかの人達とする場面だから、なるべく早めにやっといたほうがいいから。
もちろん実際には本当のエンドツーエンドを実装するのはとても難しい。なので現実的にはまあまあ仮想的な感じで基盤を実装することから始める。しかしあくまでこれが一時的なものであることを
忘れてはならない。動くスケルトンは最初の一歩である。基盤ができてテストを最初の機能に対するテストを書く準備が整ったら、システムの振る舞いを明確に定義しなくてはならない。
動くスケルトンはホワイトボードでざっくり書ける程度のものでよい。ポイントは最初のテストを書くことでプロジェクトのコンテクストを描き出すことにある。これは実際にコードを
書く前にやらなくてはならないということを表している。
勘違いしないで欲しいのだけど、これは事前に全部完全に決めておきましょう、ということではない。最低限を作ればあとはTDDサイクルで勝手に成長していく。
小刻みに本番リリースを経てフィードバックを得よう。
繰り返すように最初は苦しい。けど、どんどん楽になっていく。不確かなことは最初に明らかにしてそれから開発を進めよう。そのほうが、最初になんとなくでゆるく進めて最後にヤバイことになるプロジェクトより
ずっといい。

今日はここまで。