【RSpec】Rakeのテストの書き方
Rakeのテストの書き方。 例えば以下のようなキャンペーン作成タスクがあるとする。
# lib/tasks/campaign.rb namespace :campaign do desc 'キャンペーン作成' task :create, %i(title, start_date, end_date) => :environment do |_t, args| params = { title: args[:title], start_date: args[:start_date], end_date: args[:end_date] } Tasks::Campaign.create(params) end end
テストはこのように書ける。
# spec/lib/tasks/campaign_spec.rb require 'rails_helper' RSpec.describe 'campaign' do before(:all) do @rake = Rake::Application.new Rake.application = @rake Rake.application.rake_require 'tasks/campaign' Rake::Task.define_task(:environment) end before(:each) do @rake[task_name].reenable end context '正しいパラメータを渡したとき' do let(:task_name) { 'campaign:create' } let(:title) { 'hoge' } let(:start_date) { Time.current } let(:end_date) { Time.current } it 'キャンペーンが作成される' do expect { @rake[task_name].invoke(title, start_date, end_date) } .to change(Campaign, :count).by(1) campaign = Campaign.find_by(title: title) expect(campaign.start_date).to eq(start_date) expect(campaign.end_date).to eq(end_date) end end end
やっていることを、上から順に見ていきます。
require 'rails_helper'
spec/rails_helper.rbを読み込んでいる。 これはテスト用の設定を書くファイル。
spec/spec_helper.rbというファイルもある。 rails_helper.rbはRails固有の設定、spec_helper.rbはRSpec全般の設定を書くらしい。
rails_helper.rb内でspec_helper.rbをrequire
すればよさそう。そうすればテストファイルではrails_helper.rbだけ読み込めばよい。
参考
- RailsでRSpecによるテスト環境を構築する手順 | Simplie Post
- ruby on rails - How is spec/rails_helper.rb different from spec/spec_helper.rb? Do I need it? - Stack Overflow
before(:all)の中
before(:all) do @rake = Rake::Application.new Rake.application = @rake Rake.application.rake_require 'tasks/campaign' Rake::Task.define_task(:environment) end
1行ずつ見ていこう。
before(:all) do # hoge end
対象グループ内の全てのテストケースの前に1度だけブロックの内容をを実行する。
@rake = Rake::Application.new
Rake::Applicationオブジェクトをinitializeして、インスタンス変数@rake
に代入している。
Rake::Applicationクラスとはなんぞや。
Rake で使用するメインのクラスです。 コマンドラインで rake コマンドを実行した時に作成され、実行されます。
なるほど。 Rakeタスクを実行するのに必要なクラスということか。
では次の行。
Rake.application = @rake
Rakeクラスとはなんぞや。
Rake というコマンドラインツールを扱うライブラリです。
へえ、そうなんだ。
Rakeモジュールのソースコードを見ると…
# frozen_string_literal: true require "rake/application" module Rake class << self # Current Rake Application def application @application ||= Rake::Application.new end # Set the current Rake application object. def application=(app) @application = app end (以下略) end end
application
メソッドでRakeアプリケーションのオブジェクトをセットできるようだ。
となると、Rake.application = @rake
はRakeのアプリケーションオブジェクトとして、1行上でinitializeした@rake
をセットしているのね。
次の行に進む。
Rake.application.rake_require 'tasks/campaign'
rake_require
はなにをしているのだろう?
定義元を見てみると…
# Similar to the regular Ruby +require+ command, but will check # for *.rake files in addition to *.rb files. def rake_require(file_name, paths=$LOAD_PATH, loaded=$") # :nodoc: fn = file_name + ".rake" return false if loaded.include?(fn) paths.each do |path| full_path = File.join(path, fn) if File.exist?(full_path) Rake.load_rakefile(full_path) loaded << fn return true end end fail LoadError, "Can't find #{file_name}" end
Rubyのrequire
メソッドと似ているが、rake_require
は.rbファイルに加えて.rakeファイルも見ているとのこと。
ということはRake.application.rake_require 'tasks/campaign'
はさっき代入したRake::Applicationオブジェクトに指定のRakeファイルを読み込ませているってことか。
では次の行。
Rake::Task.define_task(:environment)
define_task
の定義元を見てみる。
# Define a task given +args+ and an option block. If a rule with the # given name already exists, the prerequisites and actions are added to # the existing task. Returns the defined task. def define_task(*args, &block) Rake.application.define_task(self, *args, &block) end
よくわからん… とりあえず[Ruby on Rails]RSpecによるRakeのテスト | DevelopersIOを読む限り、実行環境を渡しているらしい。
これで、before(:all)
の中は読めた。
まとめると、Rake::Applicationオブジェクトを生成して、テストしたいrakeファイルを読み込ませてるってことね。
before(:each)の中
では次のブロックを見てみよう。
before(:each) do @rake[task_name].reenable end
before(:each)
とbefore(:all)
の違いってなんなんだろ。
before and after hooks - Hooks - RSpec Core - RSpec - Relish
before(:each) blocks are run before each example
before(:all) blocks are run once before all of the examples in a group
なるほど、allはすべてのテストの前に1度だけ実行される。eachは各テストの前に毎回実行される。ということらしい。exampleの単位はit
なのかな?context
ではなく。
@rake
は上で作ったRake::Applicationオブジェクト。@rake['campaign:create']
でRake::Taskオブジェクトがとれる。
> @rake['campaign:create'] => <Rake::Task campaign:create => [environment]>
そしてreenable
がなんなのかというと…
reenable (Rake::Task) - APIdock
Reenable the task, allowing its tasks to be executed if the task is invoked again.
なるほど、1度実行したテストを再度実行できるようにする。 今回の例だとテストが1つしかないから不要だけど、複数テストを書く場合は必要ということか。
@rake[task_name].invoke(*args)
次のブロックへ。
context '正しいパラメータを渡したとき' do let(:task_name) { 'campaign:create' } let(:title) { 'hoge' } let(:start_date) { Time.current } let(:end_date) { Time.current } it 'キャンペーンが作成される' do expect { @rake[task_name].invoke(title, start_date, end_date) } .to change(Campaign, :count).by(1) campaign = Campaign.find_by(title: title) expect(campaign.start_date).to eq(start_date) expect(campaign.end_date).to eq(end_date) end end
let
やexpect
はRakeのテストじゃなくても使うので説明は省略。
Rake特有なのはこの部分。
@rake[task_name].invoke(title, start_date, end_date)
上でセットしたRake::Taskを実行している。
invoke
で実行できる。他にexecute
というメソッドもある。
invoke
- 引数を渡せる
- 2度実行できない(
reenable
が必要)
execute
- 引数を1つまでしかわたせない
ということのようだ。
これで、Rakeのテストが読めるようになった。 いい勉強になりました。