UnitTestから RSpec on Rails に書き換えてみた (モデル編)
次の仕事から RSpec を使ってみようかかと思い RSpec on Railsを使ってみました。まずは、勉強にと UnitTest を RSpec on Rails に書き換えてみた。
ドキュメント・参考資料
- RSpec-1.1.11: Overview : RSpec ホームページ、英語ですが例が多く実際にSpecを書くのにとても役に立ちます。
- Rubyist Magazine - スはスペックのス 【第 1 回】 RSpec の概要と、RSpec on Rails (モデル編) : 日本語の解説、長いけど一読、二読しよう!
- Rubyist Magazine - スはスペックのス 【第 2 回】 RSpec on Rails (コントローラとビュー編) : 日本語の解説、長いけど一読、二読しよう!
- WEB+DB PRESS Vol.45 YugiさんのRSpecの記事 : よくまとまっています。
早く、(日本の)書籍がでると良いですね ^^)
インストール
gem でインストール
sudo gem install rspec-rails
rspec 1.1.11, rspec-rails 1.1.11 がインストールされる
なぜか、実行時にエラーが発生。 hoe というモジュールが必要らしい
sudo gem install hoe
Spec を書く
1. まず、rspec用ディレクトリィーを作成。
cd PROJECT script/generate rspec
2. 既にモデルはあるが rspec_model で モデル用のspecを作成
% script/generate rspec_model todo exists app/models/ create spec/models/ create spec/fixtures/ :verwrite app/models/todo.rb? (enter "h" for help) [Ynaqdh] n skip app/models/todo.rb create spec/models/todo_spec.rb create spec/fixtures/todos.yml exists db/migrate Another migration is already named create_todos: db/migrate/20080816083025_create_todos.rb
できた todo_spec.rb
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe Todo do before(:each) do @valid_attributes = { } end it "should create a new instance given valid attributes" do Todo.create!(@valid_attributes) end end
3. fixtues を test/fixtues からコピー
4. UnitTest(todo_test.rb) から Spec(todo_spec.rb) を作成
# todo_test.rb require 'test_helper' class TodoTest < ActiveSupport::TestCase def test_user_relation assert_equal("山田太郎", todos(:task1).user.name, "todos.user_id の関連する users.name を取得") end def test_validates_presence_of todo = Todo.new(:due => '2008-09-01', :task=> '打ち上げ', :user_id => users(:yamada)) assert(todo.save, "正常のデータの書き込み") todo.task = '' assert(!todo.save, "taskが空のデータの書き込み") end end
# test_spec.rb require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe Todo, "の取得は" do fixtures :todos, :users it "関連したUserの名前が参照できる" do Todo.find(todos(:task1).id).user.name.should == "山田太郎" end end describe Todo, "の保存は" do before(:each) do @todo = Todo.new(:due => '2008-09-01', :task=> '打ち上げ', :user_id => 1) end it "taskが空でなければ保存できる" do @todo.save.should be_true @todo.errors.should be_empty end it "taskが空なら保存できない" do @todo.task = '' @todo.save.should_not be_true @todo.should have(1).error_on(:task) end end
5. 実行 rake spec:models または script/spec spec/models で実行できます。
script/specでは色々なオプションが設定できます。rake の場合はオプションを spec/spec.opts ファイルに指定します。
% script/spec -c -fs spec/models Todo の取得は - 関連したUserの名前が参照できる Todo の保存は - taskが空でなければ保存できる - taskが空なら保存できない User の作成は - 全ての項目が空でなければ保存できる - nameが空が空なら保存できない - emailが空が空なら保存できない - passwordが空が空なら保存できない User のパスワード作成・変更は - 新規登録ではパスワードは暗号化される - パスワードを変更しないとき、パスワードは再暗号化されない - パスワードを変更したとき、パスワードは暗号化される User の認証は - 正しいemail,passwordで認証される - 正しいくないpasswordで認証されない - 正しいくないemailで認証されない Finished in 0.39696 seconds
6. 実行の高速化
script/specの実行は、Railsライブラリー等の読み込み等で、そこそこ時間がかかります。そこで、Railsライブラリー等の読み込まれているRSpec用のサーバーを立ち上げてコマンドには --drb または -X オプションを付けて実行すると高速に実行されます。 dRubyスゴイ!!
% script/spec_server & % script/spec -c -fs --drb spec/models
しかし、私の環境ではSpecが2回実行されてしまいます。
感想
- RSpecのイディオム(.should ...) にまだ慣れてないが、なれればUnitTest同様に書ける
- UnitTestに比べるとテストが簡潔に書ける気がする
- specdocの良い書き方を習得する必要がある 日本語の良いサンプルが欲しい !! TODO: Rails勉強会@東京で聞いてみよう
次回は、コントローラ編
もう一つ書いたので載せておきます。RSpec ベテランの方 つっこみ 希望 :-)
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe User, "の作成は" do before(:each) do @user = User.new( :name => '海田太郎', :email => 'umida@rails.com', :password => 'password1') end it "全ての項目が空でなければ保存できる" do @user.save.should be_true @user.errors.should be_empty end it "nameが空が空なら保存できない" do @user.name = '' @user.save.should_not be_true @user.should have(1).error_on(:name) end it "emailが空が空なら保存できない" do @user.email = '' @user.save.should_not be_true @user.should have(1).error_on(:email) end it "passwordが空が空なら保存できない" do @user.password = '' @user.save.should_not be_true @user.should have(1).error_on(:password) end end describe User, "のパスワード作成・変更は" do before(:each) do @user = User.new( :name => '海田太郎', :email => 'umida@rails.com', :password => 'password1') end it "新規登録ではパスワードは暗号化される" do @user.save.should be_true user1 = User.find(@user.id) user1.password.should == Digest::SHA1.hexdigest('password1') end it "パスワードを変更しないとき、パスワードは再暗号化されない" do @user.save.should be_true user1 = User.find(@user.id) user1.save.should be_true user2 = User.find(user1.id) user1.password.should == user2.password end it "パスワードを変更したとき、パスワードは暗号化される" do @user.save.should be_true @user.save.should be_true user1 = User.find(@user.id) user1.password = 'password2'; user1.save.should be_true user2 = User.find(user1.id) user1.password.should == Digest::SHA1.hexdigest('password2') end end describe User, "の認証は" do before(:each) do User.new( :name => '海田太郎', :email => 'umida@rails.com', :password => 'password1').save! end it "正しいemail,passwordで認証される" do User.new(:email => 'umida@rails.com', :password => 'password1').regitered?.should be_true end it "正しいくないpasswordで認証されない" do User.new(:email => 'umida@rails.com', :password => 'password2').regitered?.should_not be_true end it "正しいくないemailで認証されない" do User.new(:email => 'umidane@rails.com', :password => 'password1').regitered?.should_not be_true end end