TDDを真面目にやってみて気付いたこと

Created on 18 Mar, 2010 (Updated on 23 Mar, 2010) | Tag(s): development tdd

何を今更、なことかもしれないないのだけど、もしかしたらこれを知ることでTDD(Test-driven development)をやることのハードルが一気に下がる人がいるかな、と思ってメモ。 特に、ある程度プログラマとして経験があるけど、どうもTDDは慣れないという人向き。


“TDDとは、TDD以前に脳内や機上でやっていたことをコードに落とすことに過ぎない”

このことが解ってから、TDDをするのが一気に苦痛ではなくなり、むしろ楽しくなった。

TDDでなくても、コーディングをするとき、temporaryなテストコードを書いたり、目視でのチェックはしたりするものだろう。たとえば、一時的に変数の値をハードコードして挙動を変えてみて、それを目視で確認したり、printデバッグとかもその一部だ。

つまり、このtemporaryなコードや目視している部分をpermanentにするのがTDDで書くテストコードだということがわかった。

TDDであろうが、なかろうが、考えないといけないこと、やらないといけないことはたいして変わらない。たいして変わらないなら、テストコード書いておいた方が得。

テストコードがあれば、チーム内で技術や知識の共有もテストコードを通じてしやすくなるし、コードレビューだって、コードとテストがセットの方がやりやすいだとやりやすい。

単純に、テストファーストでやらないのは色々と損だよね。って感じ。


後は、おまけみたいなものなんだけど、自分でTDDしたり、人のTDDでしたコードを見たりして、いくつか思ったことをメモ。

1. TDDでコードや設計がきれいになるのは、プログラマをさぼらせないから

いいコードでないとテストが書きにいので、「いいコードを書くのをさぼれない」ってことになって、結果的にきれいなコードにはなりやすい。人はさぼるものなので、これ重要。

2. TDDのUnit Testは自作自演

TDDでは、テストコードを書く人と実際のコードを書く人は通常同じなので、どうしても自分の都合のよいテストコードを書いてしまいがち。また、仕様を誤解してても、その誤解したままのテストコードになって、テストは通る。もちろん、自作自演だからこそ、1.の恩恵を得れる。

3. ある程度の範囲に影響するリファクタリング時の動作の保証は微妙

Unit Testは、どちらかというとホワイトボックス的なテストなので、リファクタリング時にもあわせて変えないといけないことも多い。そのため、ある程度の範囲を直す場合、Unit Testだけでは不十分。動作の保証という意味ではブラックボックス的なcucumberみたいなテストも組み合わせないと厳しい。(もちろん自動化以外のテストも必要だけど、これは別の話)

これはTDDってよりUnit Testの話か。

4. テスト自体の知識も大切

当然なんだけど、TDDをするようなツールの知識だけではなくて、ある程度のテストの知識がないと良いテストは書けない=良いTDDはできない。テストの知識ってのはCOBOLの時代(もっと前かな)から変わってないような知識。境界値チェックとか(ちなみに、こういうの勉強するのに情報処理試験の勉強ってなかなか侮れない)。

単純な例を挙げると、ある条件での平均値を求める、と言ったコードに対するテストで、テストコードで使うテストデータがその「ある条件」のものしか用意してなく、「ある条件」以外のものを用意してないといったケース。この場合実際のコードの方に「ある条件」が抜けていてもテストは通ってしまう。超基本なんだけど、それすらできてないテストを見かける。

単体テスト仕様書を何百枚も手書きしていた世代のノウハウをゲットすべき(関係ないけど、Key-Value Storeな方面でもあの世代のノウハウって役に立ちそう)。

ちょっと逸れるけども、知識どうこうではなくて、手を抜いてしまっているダメなテストももちろんダメ。

たとえば、Rubyとrspecでの例になってしまうが、 foo.should_not be_nil を安易に使いすぎるとか。foo.should == something とちゃんと書けるようなケースなのに、should_not be_nil で手を抜く。

他にも、Mock/Stubを使いすぎて、何もテストしてない状態になっている、とか。笑い話みたいだけどたまにある。

こういうのは「自分が何をテストしているのか」というのをしっかり意識しないと陥りがち。

そういえば、「TDDは品質のためじゃない!」 と、でかい声で言ってる人でも、多分このぐらいのことは当然やってる人たちなんだろうけど、なんか誤解は与えそうだね。とは思った。



Flash嫌い?

Created on 16 Mar, 2010 | Tag(s): development flash

今の会社に入るまで、自分も普通にFlashが嫌いだったのだけど、自分が関わるようになって「まー、ありかな」と思うところもあるのでメモ。アニメーションとかゲームとかでばりばりFlashっていう方面ではなくて、アプリケーションの一部としてFlashを使う話。

まず自分のFlashに対する経験を書いてみる。

Flashとバックエンド(サーバサイド)の間に立ってコミュニケーションをするモジュールをFlexで作った。というのが一番技術的にFlashに近づいたところかな。

これを作る前はFlashが直接バックエンド(またはJavascript経由でバックエンド)と直接コミュニケーションしていたんだけど、色々と問題(実装したFlash毎に通信方法が違ったり、通信環境のエラー処理をFlash側にそれぞれ書かなかったりいけなかったり)が出てきたので間にデータハンドリング用の層を作った。また、Flashから直接サーバサイドやJavascriptとやりとりするのを禁止にして、Flashのプログラマの人がpureなFlashの制作に集中できるようにした。

それと、ActionScriptというところで言うとAdobe Flexでそれなりの規模のアプリケーションは作ったことはある。

そんな感じなので、ActionScriptはそれなりにわかるけど、Flash固有のこと(タイムラインとか)はよくわかってないと言った感じ。でも、なんとなくFlashがどう動いているかというのはわかっているつもり。それと、それなりの数のFlashのプログラマと一緒に働いてきて、Flash開発とFlashの開発者の人の傾向みたいなものもなんとなくわかっているつもり。

で、自分が感じている開発視点でのFlashの強みは、

  • 生産性は高い? これは正直微妙なところもある。ただ、HTML+Javascript+CSSが好き(でFlashが嫌い)な人は「それ、HTMLでもできるよ」ってすぐ言うのだけど(エフェクトとかね)、やっぱり生産性、保守性という面ではまだまだFlashには敵わないかな、とは思っている。もちろんFlashが得意なところに限る。テキスト処理みたいな苦手なところで使うと一気に生産性ダウン。
  • 人材を確保しやすい。これも微妙か。数年前は、html+Javascript+cssで、きれいに動きを付けられる人を探すのは、Flashな人を探すより大変だった。最近はそうでもないのかなー。
  • オーディオやビデオの再生や録画と言ったところはまだまだFlashでしか(簡単に)できないことも多い。
  • ブラウザ間の互換性を(あまり)気にしなくていい。

弱いところは、

  • テスト系のツールが遅れてる。Unit Testがあるぐらい? 画面系の自動テストも、商用含めいくつかあるみたいだけど、あまり標準的なものもなく、結局未だに手でテストしてる。TDDだBDDだ言って盛り上げっているところから見ると、あれ?って感じ。
  • SCMが使いづらい。Flashだとまだバイナリファイルで管理している部分(fla)があって、SCMでの管理が非常にしづらい。Flexならほぼ全部テキストベースのコードで行けるのだけどね。このご時世に「いつの間にかファイルが先祖返りしてた」とかありえない。
  • バックエンドのことまで考えられる技術者が少ない? これはもう半分諦めて、インタフェースの部分はバックエンドの技術者が作ってしまった方がよさそう。
  • テキスト系が弱すぎる。Flashというより、Flash Playerの問題か。この時代に、もっさり感のあるテキスト入力とかちょっと考えられない。
  • ブラウザ間の互換性はあまり気にしなくていいけど、Flash Playerのバージョン間の互換性は知っておく必要がある。

うーん、「みんな悪く言うけど、Flashそれなりにいいもんだよ!」と思って書き始めたんだけど、こうやって書き出すと悪いところが目立つようになってしまった。あらら。

今後は、Flashが強いところをピンポイントでモジュールとして作り、htmlとJavascriptの中でそれを調和させつつうまく使う、というのが正解なんだろうな。Adobeの中の人も同じようなこと言ってたような気がするけど。で、その辺のいい感じのフレームワークみたいのがあればいいのにね。

そうなってくると、Flash技術者の人はhtmlとかJavascript/CSSの知識も持っていないとツラそう。いかにhtmlと協調して、違和感なく動くようなFlashモジュールを作れるか、というのがポイントになってくるので。

なんとなく、メモっておきました。



Gitリポジトリにあるコードを追う / コードレビュー

Created on 21 Feb, 2010 (Updated on 23 Feb, 2010) | Tag(s): development git

これまではcommitされたコードを、commit(push時)メールでなんとなく見ていたが、取りこぼしも多いし、忙しいと、つい見なくなってしまうので、なんかいい方法はないかなとここ数カ月くらいぼんやり考えていた。で、簡単なスクリプトでできそうと気づいたのでメモ。Githubに置いてあるようなオープンソースなコードとかも追いやすいんじゃないかなー。

ちなみに、このスクリプトを書く前に、コードレビューシステム的なのを導入しようかとGerritとか、Review Boardを少し試してはみた。でも、うちで使うにはちょっと大げさ過ぎるので、導入してもツールに踊らされる or 使わなくなる、という感じがしたので、とりあえずやめた。

いまいち気に入らない点としては、Gerritとかは完全にcommit単位でのレビューなんで、ちょっとしたパッチレベルならいいのだが、がりがり書いていく中ではちょっと現実的ではないかな、という感じがした。同じ機能追加/修正のために、同じ日に同一のファイルの同じような場所に複数回commitする、ということはよくあると思うが、そういう場合に1 commitずつ見る意味はあまりないかな、という感じ。それと、普段エディタで書いてるので、レビューもエディタでしたい、ってのもある。

追記: 指摘されたのだが、もちろんGitのsquash機能を使いこなせば、きれいな1 commitにまとめられることも多いだろう。でも、それでもやはり限界はあるし、綺麗なcommitセットとログを作ることに時間をかけるのも本末転倒な部分もあるかな、と。もちろん 1 commit は 1機能の追加/修正するという原則は基本的には徹底したいんだけど。それと、個人的には複数人が同じようなところ修正した場合も一個の差分として見た方がわかりやすい。

自分の中でやりたいことをまとめると、

  • 毎日レビューするという前提 > そこまで大量なコードを相手にするわけではない
  • リポジトリの(あるブランチ)中全部のコードを対象にしたい
  • 前回レビューしたところから現在までのソースの差分を見たい
  • 同一のファイルに対する複数回のcommitは一個の差分として表示
  • ただしcommitログはファイル毎に全部表示
  • レビューで問題や疑問があった場合には普段使っているBTS(Redmine)に投げる (ここはスクリプト化しない)
  • 毎日のことなので、上記のことをコマンド一発で簡単にやりたい

こんな感じのスクリプトを作った。ほとんどGitのコマンド並べただけだけど、短いので全文出しとこ。

#!/bin/sh

if [ ! -d ".git" ]; then
  echo "run in the git top directory."
  exit 1
fi

reviewed_repo="REPOSITORY_TO_STORE_REVIEWD_CODE"
reviewed_branch="BRANCH_FOR_REVIEWED_CODE"
reviewed_combined="${reviewed_repo}/${reviewed_branch}"

output_file="reviewing.patch"
rm -f ${output_file}
for file in `git diff --name-only ${reviewed_combined}`; do
  echo "==================================================================" >> ${output_file}
  git log ${reviewed_combined}.. -- $file >> ${output_file}
  echo "" >> ${output_file}
  git diff ${reviewed_combined}.. -- $file >> ${output_file}
  echo "" >> ${output_file}
done
if [ -f ${output_file} ]; then
  emacs -nw ${output_file}
  read -p "Approved?(y) " ans
  if [ "${ans}" == 'y' ]; then
    git push ${reviewed_repo} HEAD:${reviewed_branch}
  else
    echo 'ignored'
  fi
else
  echo "No unreviewed commit."
  exit 1
fi

処理の流れ: (1-2 をするようなGitコマンドがありそうだが、探せなかった)

  1. git diff --name-only で前回から変更があったファイルの一覧取得
  2. 変更があったファイル毎に、git logとdiffで自分が欲しい情報作成
  3. 作成した差分をエディタで開く
  4. エディタを終了したら、プロンプトを出して、reviewが終わったか聞く
  5. 終わった場合には、現在までのcommit情報をレビュー済branchにpush

「前回レビューしたところまで」という情報をGitにブランチとしておくことで、未読管理とかめんどくさいことを全部gitにお任せできるので楽。これを置くところはローカルでもリモートでもいいんだけど、今回は未読情報を一箇所に置いておきたのでリモートに置いている。

たったこれだけなんだけど、なかなか気に入っていて、一日の中で時間が空いたときに、reviewしたいリポジトリに行って、 rv (このスクリプトに付けてる名前) とタイプするだけで、前回レビュー時からの差分が、慣れている emacs に出てきて、レビューが開始できる。オリジナルのソースにジャンプしたい場合とかもemacsのdiffモードにお任せなので楽々。

前もってpullさえしてあれば、オフラインでも当然動くので(最後のpushは今はリモートのリポジトリにしてるのでそこだけは場合によってネットいるけど)、電車で移動中とかどこでもできるのもいい。

たいしたことではないけど、日常なことを簡単にできるようにするってのは効率化(=自分の本当にやりたいことの時間を増やす)ための一歩ですよね。

しばらくこれで行ってみて、きつくなったら、レビューシステムを再検討してみるつもり。

追記: はてブでわかりにくいという反応を頂いたので図を書いてみた。数字は上のリストの数字と対応してる。正確にはReviwed branchはローカルに持ってきてからlogとdiffをする感じ。

全体的に、なんかもっといいGit標準的なやり方がありそうで、ちょっと自信ないのだけど。



RspecとCucumberでTDD/BDDを極める (The Rspec Bookの紹介)

Created on 17 Feb, 2010 (Updated on 8 Mar, 2010) | Tag(s): development ruby rspec cucumber tdd bdd book

本の紹介第2弾。少し前、Twitter上でTDD/BDDについて盛り上がっていたので、この本を紹介してみたくなった。

The Rspec Book: Behaviour Driven Development With Rspec, Cucumber, and Friends」という本。

この本は、RspecCucumberを使い、どう考え、どうシステムを作っていくか、というをチュートリアルを交えながら紹介する構成になっている。

ただUnit Testを紹介するだけではなく、Unit TestツールであるRspecに、BDDツールであるCucumberを組み合わせて使うことで、Unit Testでカバーできない部分をCucumberで補い開発をする、というところがこの本の肝になっている。

この本を読み、実践することで、Unit Test*だけ*を書いてシステムを作っているときのモヤモヤ感をかなり解消できる。また、RspecもCucumberも知っているけど、両方をどうやって使っていくかよくわからない、という人もこの本を読むとかなりすっきりできるのでお勧め。

以下に、自分なりに理解したところを書いておく。本の内容から少し離れているところもあるので、詳しくは本を読んでみて欲しい。

また、ここでは触れないが、この本では、RspecとCucumberの基本的な使い方や、webratseleniumなどのWeb系のテストツールと一緒に使う方法や、Railsとの組み合わせ方なども含まれているので、Rubyでのテストに関するかなりの部分がカバーされていてかなりお得な作りになっている。

Unit Testとは

改めて書くまでもなく、Unit Testというのは(自動化するかどうかは関係なく)、とにかくコンポーネントとしての精度を高めるものである。また、Unit Testの優れている点として、Unit Testは「テスト」だけのためではなく、「設計」のためであるということが言える。テストを書くということ自体が設計となる。テストが書きにくい=設計が悪い、ということでもあるので、テストをきれいに書くことで、自然といい設計になるように導いてくれる。

しかし、Unit Testは、コンポーネント単位での動作は保証するが、それらを組み合わせたときに、最終的にシステムがどのような物になるか、ということまでは基本的保証しない。そして、Unit Testは、基本的にプログラマの自作自演ということもあり、外部から、システムとして見たときにどう動くかということも保証しない。また、非エンジニアの人にどういうシステムか、というのを説明するのにもUnit Testは向いていない。

これらは別にUnit Testの欠点というわけではなくて、Unit Testはそういうものなのである。

「Unit Testだけ」で開発するモヤモヤ

Unit Testでカバーできる範囲は「設計」を設計としてそのままコードに落とし、そしてそれがそのままテストにもなり、自動化もでき、再利用もできる。

しかし、Unit Testの対象外である、システムを外部から見たときの振る舞いに関して、設計やテストはコード化されずに、BTSや色々なツールで管理しても、結局「使い捨て」になりやすい。ドキュメントをコードとは別に書いても、プロジェクトが進むごとにコードとどんどん乖離していってしまう。

今まで、この部分を解消するためにいろいろなツールやプラクティスを試してみたが、どうも自分的にしっくりくるのがなかった。それは、おそらく、そういうものの多くは、ドキュメント(紙)ベースなものが多いからだと思っている。やはりプログラマとしては、そこも「コード」で定義したいし、コードに書いてあることをドキュメントにも書くということはしたくない。

Cucumberとは

このモヤモヤを解決してくるのがCucumberというツールと、それを使ったプラクティスになる。Cucumberでは、外側の設計(とテスト)をコードに落とすものである。Cucumberを使うと、ユーザから見た動作に関しての設計を、そのままコードにすることができる。Unit Testでコンポーネントに対してテストを書くのと同じ構図になる。

また、Cucumberは非常に自然言語に近い形で書けるので、非エンジニアの人が読むことが(場合によっては書くことも)可能だ。

RspecとCucumberの連携

RspecとCucumberは、それぞれ単体で使っても、もちろん便利なのだが、この本では、それらを組み合わせることで、さらに強固で柔軟なシステムを作ることができると説明している。

コンポーネントレベルでの動作の保証にはRspecを使い、ユーザから見た動作に関しての保証にはCucumberを使うということになる。

この本では、1つの機能毎に下記の各フェーズを繰り返し開発を進めて行くことを提案している。

  1. Cucumberを使い外部の設計(=テスト)を書く - この段階では実装がないのでCucumberに実行結果はRed(失敗)になる。
  2. Rspecを使い内部の設計(=テスト)をする - この段階では実装がないのでRspecを実行するとRedになる。
  3. Rspecが通るように実装する。この時点でRspecはGreen(成功)になる。
  4. Cucumberが通るようにCucumberの実装を書く。ここで、2-3で作った部分がうまく1.の設計にあわない場合、2.に戻る必要がある。場合によっては1.に戻る。

つまり、外側 (Red) - 内側 (Red) - 内側 (Green) - 外側 (Green) という順序で開発して行くことになる。

この手法は、非常にアジャイル開発とも相性がいい。上記の1から4の一回で作る機能の単位として、アジャイルの一つのユーザストーリはぴったり当てはまる。

本の入手方法

ここまで書いておいてなんなんだが、まだこの本は紙の本としては発売されてない。延期につぐ延期で、次の予定は2010/4になっている。しかしとりあえず現時点ではベータ版をPDFで買うことができる。上のリンクから買える。

一度購入しておくと、新しいベータ版が出る度にダウンロードできるので、個人的にはここ半年位楽しんでいる。英語だが、チュートリアル形式でコードも多いので英語にあまり自信がなくてもなんとかなるだろう。

Amazon日本でも紙の本の予約はできるみたい。



Web開発は難しい

Created on 13 Feb, 2010 | Tag(s): development

今さら何をってことだけどWeb開発は大変だなあっていうチラシの裏。

とにかく、知らないといけない範囲が広すぎる。

細かいことを抜きにしても、ネットワークとか、プロトコルやなんやらがあって、サーバサイドで動かすアプリケーションがあって、データベースがあって、ロードバランサとかプロキシとかが出てきて、ブラウザが複数種類あって、その上でHTMLとかJavascriptとかCSSとかFlashが走って、UIだとかUXなんかも考えないといけないし、それぞれにライブラリやフレームワークがあって、最近だとNoSQLなんかも出てきた。それらを動かすサーバやクライアントのOSや仮想化技術についても知らないといけないかもしれないし、もしかするとハードウェアの知識も必要だったりする。そうこうしているとセキュリティなんて厄介なことも出てくる。

そして、それらが大量な数のマシンで分散して構成されていたり、地球の裏側にあったりするし、さらに、それらを使って効率良く開発するために、色々な開発手法とかテスト手法とかプロジェクトマネジメントなんていうものにも手を出さないといけないかもしれない。これらとはまったく別のところで「ぎょーむちしき」や「まーけてぃんぐ」なんてものも必要になってきたりすると、もう何がなんだか。

もうほんと大変。

「俺サーバサイドやるから、お前HTMLとかJavascriptで、お前マネージメントな!」と分担ができたとしても、実際にいいチームになるには、お互いがお互いの技術をそれなりに知っている必要がある。

クラウドという甘い誘惑がでてきて「そこから向こう側は俺知らね」って雰囲気はあるが、それは自分で手を出せない部分を作るというリスクや、自分で何でもできるという自由を手放すのとトレードオフなので、安易に乗っかるわけにはいかない。たとえ乗っかるとしても、乗っかるクラウド固有の勉強はまた増えるしね。

自分の中では、愚直に勉強するしかない、ってのが今のところの結論なんだけど、そうは言ってもきりがないし、自分だけならまだいいけど、同僚などの自分の周りにいる人に「すべてを知れ!」ってのには無理があるような気もする。銀の弾丸はいつ発明されるんですか? っていう気分にもなってくる。

どうすればいいのかわからないけど、明日も頑張ろう!

これでいいのかなー。