RSpecでCSVダウンロードのcontrollerテストを書く方法をまとめていきます。
環境
- ruby 2.4.3
- Rails 5.1.5
- rspec-rails (3.7.2)
- factory_bot_rails (4.8.2)
完成したソースコード
先にソースコード貼っちゃいます。
まずはコントローラー
app/controllers/users_controllers.rb
class UsersController < ApplicationController def index @users = User.all respond_to do |format| format.html format.csv do send_data render_to_string, filename: "users_#{Time.zone.now.strftime('%Y%m%d%H%M%S')}.csv", type: :csv end end end end
続いてテスト側
spec/controllers/users_controller_spec.rb
require 'rails_helper' RSpec.describe UsersController, type: :controller do describe 'GET #index' do context 'formatがcsvの場合' do before do get :index, format: :csv end it 'CSVダウンロードが実行されること' do expect(response.headers["Content-Type"]).to include "text/csv" end it 'ファイル名が正しいこと' do expect(response.headers["Content-Disposition"]).to include "users_#{Time.zone.now.strftime('%Y%m%d%H%M%S')}.csv" end end end end
RSpecの基本
まずRSpecの基本からおさらいしておくと
RSpec.describe
(またはdescribe
)でテストの塊をつくるit *** do ~ end
でそのテスト実行により期待する結果を説明expect
~ でそのテスト実行により期待する結果を表現
という感じ。
詳しくはこちらですごく分かりやすく解説されているので割愛
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 - Qiita
format: :csv
続いてformat: :csv
について。
コントローラーのテストを書く際はget :index
のように呼び出したいアクションをコールするのだが、この時にパラメーターを渡したい場合
get :show, params: {id: @user.id}
みたいな書き方をする。
今回の場合はパラメーターではないけどformatがhtmlではなくcsvであることを表現したいので
get :index, format: :csv
このように記述。
ちなみに実行したいテストが複数ありit do~end
の中で毎回getするのが冗長なので
before do get :index, format: :csv end
このようにbefore do~end
でまとめています。
CSVダウンロードでテストすべきこと
CSVダウンロードでテストすべきこととして挙げられるのが
- 正しくリクエストが走るかどうか
- 正しいファイル名でダウンロードされるかどうか
- ダウンロードしたファイルで期待したカラムが表示されているかどうか
- ダウンロードしたファイルで期待した値が表示されているかどうか
くらいだと思う。
今回はとりあえず、CSVダウンロードのリクエストが走るかどうかと、ファイル名が正しいかどうかをテストしたい。
CSVダウンロードが実行されること
まずCSVダウンロードが実行されるかどうかはContent-Type
の中身をチェックすることで確認する。
page.response_headers
でヘッダの中身を確認できると書いている記事もあったけど、ドライバが違うからなのかNameErrorになってしまった。
自分の環境の場合はresponse.headers
で取得できた。
> response.headers => {"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"1; mode=block", "X-Content-Type-Options"=>"nosniff", "Content-Type"=>"text/csv", "Content-Disposition"=>"attachment; filename=\"users_20201115155807.csv\"", "Content-Transfer-Encoding"=>"binary", "Cache-Control"=>"private"}
よってテストはこうなる
it 'CSVダウンロードが実行されること' do expect(response.headers["Content-Type"]).to include "text/csv" end
ファイル名が正しいかどうか
response.headers
でヘッダを取得するとファイル名も確認できるので
> response.headers["Content-Disposition"] => "attachment; filename=\"users_20201115155807.csv\""
テストはこうなる
it 'ファイル名が正しいこと' do expect(response.headers["Content-Disposition"]).to include "users_#{Time.zone.now.strftime('%Y%m%d%H%M%S')}.csv" end
CSVファイルの内容をテストする
今回自分はそこまでのテストは書かなかったのだけど、
- ダウンロードしたファイルで期待したカラムが表示されているかどうか
- ダウンロードしたファイルで期待した値が表示されているかどうか
を検証したい場合は下記記事が参考になりそうだった。
Rails - .rubyテンプレートでCSVダウンロード - Qiita
(本当はここまでちゃんとテスト書いた方が良さそう^^;)
参考
GitHub - rspec/rspec-rails: RSpec for Rails 5+