箱のプログラミング日記。

えんじにあ奮闘記

【RSpec】CSVダウンロードのcontrollerテストを書く

f:id:y_hakoiri:20191102121704j:plain

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+

Rails - .rubyテンプレートでCSVダウンロード - Qiita

send_data でダウンロードするファイル名をテストする - kakakakakku blog