Create  Edit  Diff  FrontPage  Index  Search  Changes  History  Source  RSS  Note  wikifarm  Login

TDDinRails

テスト駆動開発はいいやり方です。なぜそうすべきかという抽象的な議論をしてくれるサイトはたくさんあるので、このページではそのことに深入りしません。主にこのテーマの初心者に、追いかけるための「レシピ」を提供することを目的としています。HowtoFunctionalTestやHowtoUnitTestも読んでおくべきでしょう。

注意: このページのどれも私のオジリナルではありません - Dave Thomas やAgile Web DevelopmentのDavid Heinemeier Hanssonや、さらにはBen Griffithsといった人々の知見のよせ集めです。なので、もしこれが役に立ったら - 彼らに感謝してください。もし役に立たなかったら、私を責めてください!

Nutshellでは、テスト駆動開発とは、一つの機能の実装を始めるにテストを書くことを意味します。この (すばらしい) 本 Agile Web Development with Rails によると、「テストはコードがどう動いて欲しいかの仕様とみなされます。テストが通ればコーディングが終わったことがわかります。さらにいいことには、そのアプリケーションのテストが一つ追加できてしまいます。」

ここに示すのは、私ならこのプロセスをRailsでどうやるかです。単純なユーザ認証システムのコントローラを例にとります。この時点では、実現したいもののぼんやりとしたアイデアがあるだけで、詳細なことはまだ何も考えていないものとします。

新しいコントローラを生成

これは普通のやり方でします。user_controllerを作ったとしましょう。そうするとなんと!テストが既にセットアップされているではないですか。これはいいとっかかりです。

機能テストを開く

それは 'test/functional/user_controller_test.rb' にあります。今まさに、あなたの実装について考え始めようとしています。あなたのログインシステムがどのように動くかわかりますか?

考えよう

コントローラはログインが成功するとセッションにユーザを保存すべき(should store the user in the session on a successful login)ですよね?

def test_should_store_user_in_session_on_successful_login

end

簡単でした! よろしい。さて、コントローラはログインが失敗するとセッションにユーザを保存すべきではありません (should not store the user in the session on an unsuccessful login)。

def test_should_not_store_user_in_session_on_unsuccessful_login

end

うまくいっています! 他の判断基準は「ログインが成功するとセッションに保存されたページにリダイレクトすべき(should redirect to page stored in session on successful login)」とか「二つのパスワードが一致しなければサインアップを拒否すべき(should reject signup when passwords do not match)」とかになるでしょう。あなたのコントローラがどうふるまうべきかわかっている限り、こういう切れ端をあなたのテストクラスに加えていきます。

空白を埋める

さあテストの切れ端のリストができあがりました。詳細を埋めていく必要があります。一例としてはこうなります:

def test_should_redirect_to_page_in_session_on_successful_login
  @request.session[:return_to] =  "/bogus/location"
  login_with_valid_user
  assert_redirected_to "/bogus/location"
end

当然、このテストは失敗します。まだ何も書いていませんから! でもこれはそのうち変わっていきます。

DRY!

さて、素敵なテストの束が書き上がりました。今では、それがどう実装されるべきかという確固としたアイデアをもっています。ここ に行ってみましょう。これはBen Griffithのブログで、次の手品のおかげで彼は感謝されることになります。

あなたのrailsアプリケーションのルートに、"Rakefile"というファイルがあります。これを開いて一番最後にこれをペーストします:

task :agiledox do
  tests = FileList['test/**/*_test.rb']
  tests.each do |file|
    m = %r".*/([^/].*)_test.rb".match(file)
    puts m[1]+" should:\n"
    test_definitions = File::readlines(file).select {|line| line =~ /.*def test.*/}
    test_definitions.each do |definition|
      m = %r"test_(should_)?(.*)".match(definition)
      puts " - "+m[2].gsub(/_/," ")
    end
    puts "\n"
  end
end

そして、コマンドプロンプトでrailsアプリケーションのディレクトリに移動し、コマンド rake agiledox を実行します。

そうすると、あなたのコントローラがすべきこと全部のリストがきれいにフォーマットされて出てきます:

E:\Work\AZCTrunk>
user_controller should:
 - store user in session on successful login
 - not store user in session on unsuccessful login
 - redirect to page in session on successful login
 - redirect on successful login even if no page stored in session
 - store user in session on successful signup
 - not store user in session on unsuccessful signup
 - redirect to page in session on successful signup
 - reject signup when passwords do not match
 - reject signup when username is too short
 - reject signup with multiple errors
 - destroy user in session on logout

これらは あなたのテストから取り出されたもの です。これのために、私達はテストに長くて説明文のような名前... "test_should_....on_...." をつけていたのです。

お手軽

これで、この素敵な判断基準のリストを、あなたの空っぽの user_controller.rb にコピー&ペーストすることができます。このリストは、メソッドを書いていくときの規範的な指針として利用することができます。これで、明らかなものを書き忘れたり、何かがどう動くべきかについて考えるのに時間をつぶすことがなくなります。コードを書くだけ。完成したかどうかどうやればわかるかって?えっと、もちろん、あなたのテストがパスしたときです。

いくつかテストを書いただけで、仕様のリストとそしてドキュメントのとっかかりが... 何もせずに ...得られました。

考え

この方法が素晴らしくうまくいくとわかり、私はこれを一貫して使うことにしました。でも、忘れないでください。Railsでは心変わりしてもかまいません。最初に机に向かって空っぽの "should do..." リストを書くときに、すべて をちゃんと考えていると期待されたりしません。かまわないのです。何か新しいことを考えたとしたら、テストに戻り、記入して、"rake agiledox" を動かし、そしてコーディングにまた戻ればいいのです。

コメント

"To everyone out there who actually knows what they're doing, please feel free to change this if you think you can improve on it, I'm really not an expert - I just wanted to try giving something useful to the Rails community to whom I owe so much -- Tobin Jones"

Ulyses posted ActiveRecord unittest test helper code to simplify testing AR code.

Last modified:2005/09/25 17:10:25
Keyword(s):
References: