Rails + RSpecにて、Rake Taskのテストコードを書く機会がありました。
ただ、引数が必要なRake Taskのテストを書くときに悩んだことがあったため、メモを残します。
目次
環境
- Rails 7.0.4.2
また、 rake_helper.rb
に以下の設定が記載されているものとします。
require 'rails_helper' require 'rake' RSpec.configure do |config| config.before(:suite) do Rails.application.load_tasks end config.before(:each) do Rake.application.tasks.each(&:reenable) end end
引数なしのRake Taskをテストする
以下のようなRake Taskがあるとします。
namespace :rake_option do desc 'オプションなしのタスク' task without_option: :environment do Rails.logger.error('foo') end end
開発環境でこのタスクを実行すると、ログファイルに foo
が出力されます。
# 実行 $bin/rails rake_option:without_option # 確認 $ tail -1 log/development.log foo
このタスクに対するRSpecのテストコードは以下です。
invoke
メソッドを引数無しで実行することで、Rake Taskが実行されます。
require 'rake_helper' RSpec.describe 'rake_option', type: :task do before do # loggerをモック allow(Rails.logger).to receive(:error) end describe 'without_option' do subject(:task) { Rake.application['rake_option:without_option'] } it 'ログに出力されること' do task.invoke expect(Rails.logger).to have_received(:error).with('foo') end end end
引数ありのRake Taskをテストする
Rake Taskへ引数を渡すには、いくつか方法があるようです。
4 Ways to Pass Arguments to a Rake Task | Sean C Davis
今回は
[]
を使って値を渡すパターンENV
を介して値を渡すパターン
の2つをためします。
[]
を使って値を渡すパターンのRake Taskをテストする
[]
を使ってRake Taskに値を渡す場合、Rake Taskの定義は以下となります。
namespace :rake_option do desc '[]で囲った引数ありのタスク' task :with_option, [:foo, :bar] => :environment do |_task, args| Rails.logger.error("#{args[:foo]}, type => #{args[:foo].class}") Rails.logger.error("#{args[:bar]}, type => #{args[:bar].class}") end end
開発環境でこのタスクを実行するには、[]
で引数の値を囲います。
なお、カンマの前後に空白を付けてしまうと動作しなくなります。
Ruby on Railsの rake taskの挙動について
# 実行 $ bin/rails rake_option:with_option[1,true] # 確認 $ tail -2 log/development.log 1, type => String true, type => String
このRake Taskに対するテストコードは以下となります。
invoke
メソッドの引数に、[]
内の値を ,
で区切って渡しています。
RSpec.describe 'rake_option', type: :task do before do # loggerをモック allow(Rails.logger).to receive(:error) end describe 'with_option' do subject(:task) { Rake.application['rake_option:with_option'] } it 'ログに出力されること' do task.invoke('foo', 'true') expect(Rails.logger).to have_received(:error).with('foo, type => String') expect(Rails.logger).to have_received(:error).with('true, type => String') end end end
ENV
を介して値を渡すパターンのRake Taskをテストする
ENV
を介してRake Taskに値を渡す場合、Rake Taskの定義は以下となります。
なお、今回は引数の指定を必須とするため、ENVから値を取得するときに fetch
を使います。
ENV.fetch のすすめ - ~saiya/hatenablog
namespace :rake_option do desc 'ENVに入る引数のタスク' task with_env: :environment do foo = ENV.fetch('foo') bar = ENV.fetch('bar') Rails.logger.error("#{foo}, type => #{foo.class}") Rails.logger.error("#{bar}, type => #{bar.class}") end end
開発環境でこのタスクを実行するには、引数をスペースで区切って key=value
の形で指定します。
# 実行 $ bin/rails rake_option:with_env foo=3 bar=false # 確認 $ tail -2 log/development.log 3, type => String false, type => String
このRake Taskに対してテストを書くときには、以下に注意します。
ENV
を介して引数の値を取得するため、ENV
をモックする必要があるENV
からの取得がフレームワークなどの意図しないところで発生したときにテストが落ちないよう、and_call_original
を使用する
注意点を踏まえたテストコードは以下です。
RSpec.describe 'rake_option', type: :task do before do # loggerをモック allow(Rails.logger).to receive(:error) end describe 'with_env' do subject(:task) { Rake.application['rake_option:with_env'] } before do allow(ENV).to receive(:fetch).and_call_original # fooとbar以外は元の実装のままにする allow(ENV).to receive(:fetch).with('foo').and_return('1') allow(ENV).to receive(:fetch).with('bar').and_return('false') end it 'ログに出力されること' do task.invoke('foo=1', 'bar=false') expect(Rails.logger).to have_received(:error).with('1, type => String') expect(Rails.logger).to have_received(:error).with('false, type => String') end end end
ちなみに、ENV
をモックしているところをコメントアウトすると
describe 'with_env' do subject(:task) { Rake.application['rake_option:with_env'] } before do allow(ENV).to receive(:fetch).and_call_original # fooとbar以外は元の実装のままにする allow(ENV).to receive(:fetch).with('foo').and_return('1') # allow(ENV).to receive(:fetch).with('bar').and_return('false') end #...
以下のように KeyError
となります。
KeyError: key not found: "bar"
ソースコード
Githubに上げました。
https://github.com/thinkAmi-sandbox/rails_7_0_minimal_app
今回のプルリクはこちら
https://github.com/thinkAmi-sandbox/rails_7_0_minimal_app/pull/12