第16回Rails勉強会
http://wiki.fdiary.net/rails/?RailsMeetingTokyo-0016
時間をおくと先月のように書かなくなってしまいそうなのでとりあえず書く。
今回はやや人が少なめだった。
RSpecについて
RSpecを使うとTest/Unitを使うことよりどんなことがうれしいかということの再確認をした。
個人的には
- とりあえずテストに入るまでの書く量が少なくて済む(専用コマンドがあるため)
- 何をテストするのかのリストを出力できる。
- 標準で色がつく
- expect, actualの順に書かなくても良い。(エラーメッセージを気にしなければ別に逆でも問題ないよねと舞波氏
- テストの切り分けがクラス単位意外でもしやすい
- context_setupとcontext_teardownがある。
- Mock機能がついている(test/unitでもFlexMockとかMochaを併用すればOK
これぐらいかなと思う。
今までは,
@stack.should_empty
のように書いてきたのだが,最新のバージョンだとobsolatedになるそうな。
@stack.should be(:empty)
と書くようになるらしい。個人的にはエディタの動的略称展開が使えるので前の方が良かった。
その代わり,trunkだとspecifyの引数の文字列を省略しておくと,ブロック内のコードからメッセージを自動生成してくれる機能がついているらしい。これは便利かなと思った。
ActiveRecordを雰囲気Drivenから脱出する
ActiveRecordのよく分からないところを実際に試して検証してみようというセッション。
主に関連づけられたモデルはいつDBに保存されるのかということについて検証してみた。
会場では./script/runnerを使って検証した。何度もデータが同じ状況で実験するためにはテストとして書いてしまった方が良いとのことなので,このログはテストコードでまとめる。*1
まずサンプルのモデルの構造から
class CreateShops < ActiveRecord::Migration def self.up create_table :shops do |t| t.column :name, :string, :null => false end end def self.down drop_table :shops end end class CreateServings < ActiveRecord::Migration def self.up create_table :servings do |t| t.column :shop_id, :interger, :null => false t.column :menu_id, :interger, :null => false t.column :created_at, :datetime, :null => false t.column :updated_at, :datetime, :null => false end end def self.down drop_table :servings end end class CreateMenus < ActiveRecord::Migration def self.up create_table :menus do |t| t.column :name, :string, :null => false t.column :price, :integer, :null => false end end def self.down drop_table :menus end end
次にモデルで関連付けの設定をする
class Menu < ActiveRecord::Base has_many :shops, :through => :servings has_many :servings end class Serving < ActiveRecord::Base belongs_to :shop belongs_to :menu end class Shop < ActiveRecord::Base has_many :menus, :through => :servings has_many :servings end
適当にfixtureを書く
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html katsudon: id: 1 name: カツ丼 price: 500 tendon: id: 2 name: 天丼 price: 600 chukadon: id: 3 name: 中華丼 price: 450 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html one: id: 1 shop_id: 1 menu_id: 1 two: id: 2 shop_id: 1 menu_id: 1 three: id: 3 shop_id: 1 menu_id: 1 # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html washoku: id: 1 name: 和食屋 youshoku: id: 2 name: 洋食屋
ShopにMenuを追加してみる
require File.dirname(__FILE__) + '/../test_helper' class ShopTest < Test::Unit::TestCase fixtures :shops # Replace this with your real tests. def setup @shop = Shop.find(2) end def test_add_menu_failed assert_raise ActiveRecord::HasManyThroughCantAssociateNewRecords do menu = Menu.new :name => "シチュー", :price => 500 @shop.menus << menu # servingsが存在していないため例外発生 end end def test_add_menu_success menu = Menu.new :name => "シチュー", :price => 500 menu.save! @shop.menus << menu # @shop.updateをかけなくてもservingsは保存される assert(Menu.find_by_name("シチュー")) assert(Serving.find_by_menu_id(Menu.find_by_name("シチュー").id)) end end
Belongs_to側の場合
require File.dirname(__FILE__) + '/../test_helper' class ServingTest < Test::Unit::TestCase fixtures :menus fixtures :servings # Replace this with your real tests. def setup @servings = Serving.find(1) end # セーブする前にupdateをかけてしまうとどうなるか def test_add_add_and_update_before_save_menu assert_not_nil(@servings.menu) @servings.shop = Shop.find_by_name "洋食屋" @servings.menu = Menu.new :name => "シチュー", :price => 500 assert_nil(Menu.find_by_name("シチュー")) @servings.update # 例外は起こらない? assert_nil(Menu.find_by_name("シチュー")) assert_equal("シチュー", @servings.menu.name) # 新しいデータも残ったまま assert_equal("洋食屋", @servings.shop.name) # 新しいデータも残ったまま invalid_serving = Serving.find(1) assert_equal("カツ丼", invalid_serving.menu.name) # まだ更新されてない assert_equal("洋食屋", invalid_serving.shop.name) # Shopは更新される!! @servings.menu.save! @servings.update valid_serving = Serving.find(1) assert(Menu.find_by_name("シチュー")) assert_equal("カツ丼", valid_serving.menu.name) # 一度updateをかけてしまうとダメ end end
あとはcreateとnewの違いとか,そこら辺を確認した。
ちなみにTransactionが有効かどうかのテストを書くときはテストDBをMySQLのInnoDBなどにしなければならない模様。
あとActiveRecordからSQLiteでBlobを保存すると画像が壊れると聞いたのでそれの実験もした。それに関しては,つかもとさんがパッチを作ってくれた模様。GJ!
それにしても今実験するのにRSpecやっぱり使わなかった。。。やっぱり標準でサポートされてることは大きい。
*1:記憶を便りに書いているので会場でやったときとは例題も違う点に注意