RSpecでsend_fileの送信するファイルの内容をテストする方法(修正版)
コントロラーのRSpecを書いていて send_file の response が上手くテスト出来なかったので調べてみました。ただし調べたのは Rails 2.3.8 , Rspec Rails 1.3.3 です。
@irohirokiさんから ActionController::TestProcess#binary_content を使えば良いですとTweetがあったので変更しました。
下のようなコントロラーのメソッドが在ったとき
def download ・・・ send_file "ファイルのパス", :type => 'application/octet-stream' ・・・ end
RSpecを素直に書くと
it "POST /download はファイルの内容を戻す" do post "download" response.body.should == IO.read("ファイルのパス") end
のようになりますが、これでは以下のようなエラーになってしまいます。
'XxxxxController POST /download はファイルの内容を戻す' FAILED expected: "・・省略・・", got: #<Proc:0x0000000102372000@/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.8/lib/action_controller/streaming.rb:93> (using ==)
以下のように ActionController::TestProcess#binary_content を使うと簡単にテストできました
it "POST /download はファイルの内容を戻す" do post "download" response.binary_content.should == IO.read("ファイルのパス") end
ここからは古い記事(binary_contentがやってる事もほぼ同じでした)
どうもsend_file は 無名関数 (Proc) を戻しているようです。ソース action_controller/streaming.rb を見ると
render :status => options[:status], :text => Proc.new { |response, output| logger.info "Streaming file #{path}" unless logger.nil? len = options[:buffer_size] || 4096 File.open(path, 'rb') do |file| while buf = file.read(len) output.write(buf) end end }
戻している無名関数はファイルの内容をoutput引数へ出力する関数です。そこでこの関数に文字列ストリーム(StingIO)を渡してあげればファイルの内容が取得出来そうです、以下のようなコードを書いたところ無事 sned_fileの出力がテスト出来ました :-)
require 'stringio' def receive_from_send_file(proc) stream = StringIO.new('', 'w+') proc.call(nil, stream) stream.string end it "POST /download はファイルの内容を戻す" do post "download" receive_from_send_file(response.body).should == IO.read("ファイルのパス") end