RspecのControllerテストでDragonflyのモデルに新規レコードを登録する
RspecのControllerテストでの添付ファイルの作り方がよくわからなかったのですが、こんな感じでできましたのでメモ。
Dragonflyのモデル定義のphoto.rbはこんな感じ。
class Photo < ActiveRecord::Base attr_accessible :photo_image end
photo_controller_spec.rbはこんな感じです。
describe PhotosController do filepath = "/OK.jpg" post :create, photo: { photo_image: fixture_file_upload(filepath, 'image/jpg') } it do response.response_code.should == 200 end end
fixture_file_uploadにアップロードしたいファイルのパスを渡して、content-typeを指定するだけで、temp fileとしてアップロードしてくれます。
filepathは、"$APP_ROOT\spec\fixtures" 以下のパスを指定します。
ではでは。
devise + Rspec でログイン状態のテストをしようとしたら動かなかった
今回も書いてあるとおりにやっていたつもりなのですが、動きませんでした。
そして、よく見たら書いてある通りにやっていなかったというお話です。
やり方はここに書いてある通りです。
書いてある通りに書いた(つもり)specがこれ。
describe "ログインユーザの POST" do before do login_user # ログイン @model = build(:model) post :create, model: @model.attributes end it { response.should be_success } end
そんでもってコンソールに出たエラーがこれ。
undefined local variable or method `login_user' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_12:0x96e6730>
何がわるかったかというと、login_userをコールする位置でした。
理由まで調べていませんが、beforeやit method の中でコールするとこのエラーが出ます。
参考URLに書いてある通り、describeのすぐ後でコールしたらうまくいきました。
こんな感じ。
describe "ログインユーザの POST" do # ここでコール! login_user before do @model = build(:model) post :create, model: @model.attributes end it { response.should be_success } end
書いてある通りにやっていないので動かないの巻。
model.create / model.save は成功するのに model.all.count = 0になる on Rspec + FactoryGirl
SQLもWebアプリも難しいですね。
しょうもない事でハマってしまいました。
model単体のテストをRspec + FactoryGirlで書いていたのですが、model.create / model.save は成功する(trueが返ってきてレコードがちゃんとできる)のに、model.all.countはいつも0で、DBには保存されていないように見えるという不思議な現象に悩んでおりました。
で、原因はmodelの定義で設定していたdefault_scopeでした。
関連している他のモデルの属性を使ってソートするdefault_scopeを設定してたのですが、今回のテストには関連先まで定義していなかったため、どんなにレコードを登録しても検索条件には引っかからずmodel.allは常に0という事が起こっていたのです orz。
で、モデル単体のテストときは全てのスコープを無効にするようにしたら、ちゃんと登録済レコードが見えるようになりました。
こんな感じ。
it {
Model.unscopded {
テストコード
}
}
でも、これだとまた忘れた頃に同じ事ではまりそうなので、結局default_scopeで設定するのをやめて、名前付きスコープで定義するようにしましたです。
ではでは。
Deviseでconfirmableを設定しているときのFixtureの書き方
Deviseでconfirmableを設定していると、普通にemailとpasswordだけでFixtureを用意してもうまく行きませんでした。
user.createの時にconfirmation mailを送るため、それでこけているようです。
createの前にskip_confirmation!を設定しても良いのですが、下記方法でconfirmedなUserと認識してくれたので、これで行こうと思います。
[FactoryGirlの例]
FactoryGirl.define do factory :user do email "test@test.org" password "testtest" confirmed_at Time.now # これでConfirmedユーザー end end
ではでは。
ポリモーフィック関連のfactoryの書き方 on FactoryGirl
こんな感じで、polymorphic関連を設定しているとします。
# Photo model class Photo < ActiveRecord::Base belongs_to :category, :polymorphic => true end # Mountain model class Mountain < ActiveRecord::Base has_one :photo, :as => :category end # River model class River < ActiveRecord::Base has_one :photo, :as => :category end
そんでもって、こんなfactoryを書きます。
FactoryGirl.define do factory :mountain_photo, class: "Photo" do association :category, factory: :mountain end factory :river_photo, class: "Photo" do association :category, factory: :river end factory :moutain do name "Mt.hoge" end factory :river do name "Riv.fuga" end end
これで "FactoryGirl.create(:mountain_photo)" とやると、:categoryに:mountainが入ったレコードが、"FactoryGirl.create(:river_photo)" とやれば、:categoryに:riverが入ったレコードが生成されるようになります。
ではでは。
Dragonflyの画像ファイルを持つphotoモデルをRspec + FactoryGirl で testする
Dragonflyで画像ファイルを管理しているのですが、test方法のメモです。
テスト環境は Rspec + FactoryGirl ですが、Unit testとFixtureでも同じだと思います。
Dragonflyはtest時以下ディレクトリからファイルを探します
public\system\dragonfly\test\YYYY\MM
登録しようとする画像ファイル名が"IMG_0001.jpg"だとすると、Dragonflyの生成するuidはYYYY/MM/DD_HH_MM_SS_MLS_IMG_0001.jpgになるので、上記ディレクトリに "DD_HH_MM_SS_MLS_IMG_0001.jpg"という名前で画像ファイルを置けばよいです。
photo_spec.rbはこんな感じです。
# -*- coding: utf-8 -*- require 'spec_helper' describe Photo do describe "画像ファイル" do before do @jpg = create(:photo, photo_image_name: "OK.jpg", photo_image_uid: "2013/08/01_00_00_00_001_OK.jpg") end it { @jpg.should_not be_new_record } end end
上記specだと "public\system\dragonfly\test\2013\08\01_00_00_00_001_OK.jpg" というファイルを準備しておけば良いです。
参考までにphotoモデルはこんな感じ。
class Photo < ActiveRecord::Base attr_accessible :photo_image # For dragonfly image_accessor :photo_image end
ではでは。
limitの値によってクエリの結果が異なる
先日、"関連付けたモデルの属性で、関連元モデルをソートする"というエントリを書きましたが、これ、一見うまくいっているように見えるだけで、所望の結果が得られない場合があることがわかりました。
Model.allで全レコードを取得したときにしか正しい結果が得られません。
paginationやlimitを使って取得レコード数を制限している場合は、レコードの内容によっては所望の結果が得られない場合があります。
結論から先に言うと、:include ではなく :joinsを使用するというのが正解のようです。
こんな感じ。
default_scope :joins => :report, :group=> "spots.id", :order => "reports.created_at DESC"
SQLを直接発行してうまくいくクエリをActivRecordに書きなおしただけですが、これでうまくいきました。
さて、うまくいかない状況をもう少し詳しく説明します。
先日のエントリで紹介した設定は下記の通り。
class Spot < ActiveRecord::Base has_many :report default_scope :include => :report, :order => "reports.created_at DESC" end class Report < ActiveRecord::Base belongs_to :spot end
これで、spot.all すると、新しいReportを持っている順にspotがsortされて取得できます。
結果も正しい。
でも spot.limit(8)とかやると、spot.allのときと順番が違ったり、取得できるレコードまで違ったりするのです。
生成されたクエリを直接SQLで発行すると、limitが付いている場合でも正しい結果が取得できたりと、わけがわかりません。
色々と実験したのですが、:includeの場合は、limitで指定したレコード数分のspotレコードに紐付いているreportだけをロードしてきて、その中でソートをかけるという動作をしているように見えます。
なので、allじゃないと正しい結果にならない場合があるようです。
SQLちゃんと勉強していればわかる内容なのでしょうが、なんでこんなことになるのかよくわかっておりません。。。