これを参考にしてCarrierWaveを使った画像アップロード機能を実装してみた。
Rails 超お手軽な画像アップローダー CarrierWave の使い方 | Workabroad.jp
↑のサイトではCapybaraを使ったインテグレーションテストの書き方は載っているけど、コントローラのテスト方法が載っていなかったのでちょっとだけ詰まった。
言い換えると、今回はファイルを添付してアップロードするフォームのテストを書くというのと同義。
サンプル
フォーム
Userモデルには名前とプロフィール画像があって、更新フォームで名前とか画像をセットできることにする。
= form_for(@user, html:{ method: :put, role: "form" }) do |f|
.form-group
= f.label :image, "プロフィール画像"
= f.file_field :image, class:"form-control"
.form-group
= f.label :name, "名前"
= f.text_field :name, :autofocus => true, class:"form-control"
こんなフォーム。

コントローラ
1class UsersController < ApplicationController
2
3 def edit
4 end
5
6 def update
7 @user = User.find(params[:id])
8 if @user.update(user_params)
9 redirect_to edit_user_url(@user)
10 else
11 render :edit
12 end
13 end
14
15private
16
17 def user_params
18 params.require(:user).permit(:name, :image)
19 end
20end
updateメソッドがあって、nameとimageを更新できるようにpermitしている。
テスト要画像の用意
#{Rails.root}/spec/fixturesにテスト要のファイルを配置する。今回は画像ファイルなので、適当なファイルをsample.pngという名前で置いておくことにした。
コントローラのテスト
1RSpec.describe UsersController, :type => :controller do
2 describe "PUT update" do
3 before do
4 # テストユーザーでログインしておく
5 @user = login_user
6
7 # 先ほどのファイル
8 @file = fixture_file_upload("sample.png", "image/png", true)
9 end
10
11 it "アップロードが正常に終わり、@user.image?がtrueを返す" do
12 put 'update', id: @user.id, user: {
13 id: @user.id, name: "あたらしいなまえ", image: @file
14 }
15 expect(@user).to be_image
16 end
17 end
18end
fixture_file_uploadでfixturesディレクトリに置いたファイルをロードできる。そして、コントローラのupdateへのPUTリクエストをファイルを含めて呼び出すことでアップロードの完了を確認している。
最後のこの部分
1expect(@user).to be_image
が直感的ではないけど、RSpecのルールに従って書くとそうなるというだけで、↓のように書いても問題はない。
1expect(@user.image?).to eq(true)
fixture_file_upload
ちなみにfixture_file_uploadっていうメソッドはActionPackの中で定義されていて、見たところファイルアップロードのテストに使うRack::Test::UploadedFileを少し便利に使うために見える。
1# actionpack-4.1.0/lib/action_dispatch/testing/test_process.rb
2module ActionDispatch
3 module TestProcess
4 def fixture_file_upload(path, mime_type = nil, binary = false)
5 if self.class.respond_to?(:fixture_path) && self.class.fixture_path
6 path = File.join(self.class.fixture_path, path)
7 end
8 Rack::Test::UploadedFile.new(path, mime_type, binary)
9 end
10 end
11end