(2010-08-30: Rails 3.0.0がリリースされたのでそれにあわせて更新。generator関連が少し変わってる)
前回 の続き。
主に FactoryGirl と MongoMapper の話。基本的に何も考えなくてもそのまま使えるのだけど。
まず、設定。
spec_helper.rb で、
config.use_transactional_fixtures = true
を
config.use_transactional_fixtures = false
にする。そうしないと、ActiveRecord::TestFixtures が呼ばれてしまうのだが、ActiveRecordを入れてないので落ちる。
そもそもMongoDBにはトランザクションとかないのでfalseでいい。
後は、ActiveRecordで使う場合と同じように、FactoryGirlの定義の読み込みを spec_helper.rb の中で行う。
Factory.find_definitions
mongoDBにはトランザクションがないので、テストの前にデータを自分で消しておいたほうがいいだろう。
config.before(:each) do
MongoMapper.database.collections.each {|collection| collection.remove}
end
全体的にはこんな感じ。generatorが作ったものに、上記変更を入れただけ。
# This file is copied to spec/ when you run 'rails generate rspec:install' ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails'
# Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
require 'factory_girl' Factory.find_definitions
RSpec.configure do |config| # == Mock Framework # # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: # # config.mock_with :mocha # config.mock_with :flexmock # config.mock_with :rr config.mock_with :rspec
# config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, comment the following line or assign false # instead of true. config.use_transactional_fixtures = false
config.before(:each) do MongoMapper.database.collections.each {|collection| collection.remove} end end
試しに一つFacotryとテストを書いてみる。
File: spec/factories/entry.rb
Factory.define :entry do |f|
f.title "MyString"
f.body "MyString"
end
簡単なお試し用のテストを書いてみる。(テスト自体に意味はない)
require 'spec_helper'
describe Entry do it "should create 10 entries" do 10.times { Factory(:entry) } Entry.count.should == 10 end end
書いたテストの実行
$ bundle exec rspec spec/models/entry_spec.rb .
Finished in 0.03308 seconds 1 example, 0 failures
すばらしい!
FactoryGirlで、MongoMapper::Document同士のassociationも問題なく書ける。(ただし、MongoMapper::EmbeddedDocument から 親Documentへのassociationはうまく作ってくれなかった(正格には作ってくれるんだけど、Saveされない))
こんな感じで、Rails 2 + ActiveRecord時代とほとんど変わらない環境が Rails 3 + MongoMapper で、できたかな。
(2010-08-30: Rails 3.0.0がリリースされたのでそれにあわせて更新。generator関連が少し変わってる)
会社用の、小物Webアプリを作ろうかと思い、せっかくなのでRuby on Rails 3でmongoDB使ってみようかな、と思い、とりあえず環境を作るところまでのメモ。
Rails 3 のインストール
とりあえず Rails 3 のインストール。Bundlerで入れる。Bundler自体のバージョンが1.0以上でないとダメみたいなんで、もしそれ未満しか入っていない場合にはBundlerのインストールからする。
プロジェクトのトップディレクトリとなるところを作成し、そこにGemfileを作る。
$ mkdir ~/workspace/hoge_prj
$ cd ~/workspace/hoge_prj
Gemfile
source 'http://rubygems.org'
gem "rails", ">=3.0.0"
rails 3のgemのインストール
$ bundle install
railsコマンドでrailsプロジェクトを作成する。
$ bundle exec rails new . --skip-activerecord
このコマンド自体がGemfileをもう一度作る。上書きしてしまえばいい。MongoMapperを使う予定なので --skip-activerecord
を指定した。
次に、自分が必要なライブラリをGemfileに追加して、もう一回 bundle install する。
今回は、
gem "mongo_mapper"
gem "bson_ext"
gem "rails3-generators"
gem "haml"
gem "haml-rails"
gem "jquery-rails"
gem "rspec-rails", ">= 2.0.0.beta.13", :require => nil
gem "factory_girl_rails", :require => nil
を追加した。 rspec / haml / factory_girl は普段も使ってるのであまり考えずにそのまま使ってみる。Rails 3では、rspec 2が必要らしい。
$ bundle install
次に、config/application.rb を開いて、
config.generators do |g|
g.orm :mongo_mapper
g.template_engine :haml
g.test_framework :rspec, :fixture => true
g.fixture_replacement :factory_girl, :dir => "spec/factories"
end
をそれっぽいところに書く。
rspecとjqueryの関連ファイルを作成する。
$ ./script/rails g rspec:install
$ ./script/rails g jquery:install
今回は Mac上にインストールしたmongoDB を使っている。
次にmongodbの設定をする。
$ ./script/rails g mongo_mapper:install test_project —host=localhost
test_project の部分はmongoDBで使うデータベース名を設定する。これは単純にconfig/database.mongo.ymlに書かれるだけなので変えたければ後で簡単に変えられる。
これで、ほぼインストール完了。 rails3-generators のおかげで mongo_mapper を使った scaffold を作ることもできる。
$ ./script/rails g scaffold entry title:string body:string
invoke mongo_mapper
create app/models/entry.rb
route resources :entries
invoke scaffold_controller
create app/controllers/entries_controller.rb
invoke haml
create app/views/entries
create app/views/entries/index.html.haml
create app/views/entries/edit.html.haml
create app/views/entries/show.html.haml
create app/views/entries/new.html.haml
create app/views/entries/_form.html.haml
invoke rspec
create spec/controllers/entries_controller_spec.rb
create spec/views/entries/edit.html.haml_spec.rb
create spec/views/entries/index.html.haml_spec.rb
create spec/views/entries/new.html.haml_spec.rb
create spec/views/entries/show.html.haml_spec.rb
invoke helper
create spec/helpers/entries_helper_spec.rb
create spec/routing/entries_routing_spec.rb
invoke test_unit
create test/integration/entry_test.rb
invoke helper
create app/helpers/entries_helper.rb
invoke rspec
invoke stylesheets
create public/stylesheets/scaffold.css
とりあえず、コンソールからレコード(mongoDB風に言うとドキュメント)が作成できるか確認してみる。
% ./script/rails console
Loading development environment (Rails 3.0.0)
irb(main):001:0> Entry.create!(:title => "HOGEHOGE", :body => "Hello")
=> #<Entry body: "Hello", title: "HOGEHOGE", _id: BSON::ObjectId('4c7bc200a90e0844f8000001')>
irb(main):002:0>
簡単!
本当に保存されたか、mongoシェルで確認してみると、
% ~/somewhere/mongodb-osx-x86_64-1.5.3/bin/mongo
MongoDB shell version: 1.5.3
connecting to: test
type "help" for help
> use hoge-development
switched to db blog-development
> db.entries.find()
{ "_id" : ObjectId("4c7bc200a90e0844f8000001"), "title" : "HOGEHOGE", "body" : "Hello" }
>
入ってる!
サーバを起動して、画面でも見てみる。
$ ./script/rails server
ブラウザで http://localhost:3000/entries/ を開いて確認。
面白く無いいつもの scaffold がちゃんとmongoDBで動いているのが確認できる。
とりあえずここまでで、RSpec / factory_girl あたりを 次回 。
参考:
http://www.mongodb.org/display/DOCS/Rails+3+-+Getting+Started
http://paulbarry.com/articles/2010/01/13/customizing-generators-in-rails-3
http://groups.google.com/group/mongomapper/browse_thread/thread/cd89810b98eb7abf
http://github.com/rspec/rspec-rails
http://stackoverflow.com/questions/3004489/mongodb-initialization-error-in-rails
「The Rspec Book: Behaviour Driven Development With Rspec, Cucumber, and Friends」という本。
この本は、RspecとCucumberを使い、どう考え、どうシステムを作っていくか、というをチュートリアルを交えながら紹介する構成になっている。
ただUnit Testを紹介するだけではなく、Unit TestツールであるRspecに、BDDツールであるCucumberを組み合わせて使うことで、Unit Testでカバーできない部分をCucumberで補い開発をする、というところがこの本の肝になっている。
この本を読み、実践することで、Unit Test*だけ*を書いてシステムを作っているときのモヤモヤ感をかなり解消できる。また、RspecもCucumberも知っているけど、両方をどうやって使っていくかよくわからない、という人もこの本を読むとかなりすっきりできるのでお勧め。
以下に、自分なりに理解したところを書いておく。本の内容から少し離れているところもあるので、詳しくは本を読んでみて欲しい。
また、ここでは触れないが、この本では、RspecとCucumberの基本的な使い方や、webratやseleniumなどの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つの機能毎に下記の各フェーズを繰り返し開発を進めて行くことを提案している。
- Cucumberを使い外部の設計(=テスト)を書く - この段階では実装がないのでCucumberに実行結果はRed(失敗)になる。
- Rspecを使い内部の設計(=テスト)をする - この段階では実装がないのでRspecを実行するとRedになる。
- Rspecが通るように実装する。この時点でRspecはGreen(成功)になる。
- Cucumberが通るようにCucumberの実装を書く。ここで、2-3で作った部分がうまく1.の設計にあわない場合、2.に戻る必要がある。場合によっては1.に戻る。
つまり、外側 (Red) - 内側 (Red) - 内側 (Green) - 外側 (Green) という順序で開発して行くことになる。
この手法は、非常にアジャイル開発とも相性がいい。上記の1から4の一回で作る機能の単位として、アジャイルの一つのユーザストーリはぴったり当てはまる。
本の入手方法
ここまで書いておいてなんなんだが、まだこの本は紙の本としては発売されてない。延期につぐ延期で、次の予定は2010/4になっている。しかしとりあえず現時点ではベータ版をPDFで買うことができる。上のリンクから買える。
一度購入しておくと、新しいベータ版が出る度にダウンロードできるので、個人的にはここ半年位楽しんでいる。英語だが、チュートリアル形式でコードも多いので英語にあまり自信がなくてもなんとかなるだろう。
Amazon日本でも紙の本の予約はできるみたい。