I am developing a ROR app that relies on many custom Rake tasks.
What is the best way to test them?
This is pretty well covered in another SO question
My recommendation, repeated here in summary, is not to try testing rake tasks: extract the code to be tested to a class, model or lib function and test it there. Then just use rake to call the functions and manage dependencies.
Well, now there's a Gem for that:
Fantaskspec - https://github.com/crismali/fantaskspec
I like the PivotalLabs' way the most. However, I generalized and modified their code as followed in my app:
# file: spec/tasks/my_rake_file_spec.rb
require 'spec_helper'
require 'rake'
describe 'my_rake_file.rake' do
before :all do
Rake.application.rake_require 'tasks/my_rake_file'
Rake::Task.define_task(:environment)
end
let(:run_rake_task) {
Rake::Task[task_name].reenable
Rake.application.invoke_task task_name
}
describe 'my_task_name' do
let(:task_name) { "my_task_name" }
it "creates 10 cars" do
run_rake_task
Car.count.should == 10
end
end
end
Also, I extracted the heavy lifting of my_rake_file.rake's code to a module, which is stored in lib/
Rake tasks do need testing, especially if you do some batch processing in it. Testing them is quite easy these days:
class CleanExamsTaskTest < ActiveSupport::TestCase
def test_deletes_results_of_old_exams
travel_to Time.zone.parse('2010-07-05 10:00')
@exam.update!(end: Time.zone.parse('2010-07-04 09:59'))
assert_difference -> { @exam.answered_questions.count }, -1 do
capture_io { run_task }
end
end
def test_output
travel_to Time.zone.parse('2010-07-05 10:00')
@exam.update!(end: Time.zone.parse('2010-07-04 09:59'))
assert_output("Exams processed: 1\n") { run_task }
end
private
def run_task
Rake::Task['exams:clean'].execute
end
end
Full example
Tested with Ruby >= 1.9 and Rails 5.2. Uses Minitest
from stdlib
. No other dependencies.
Something like:
def execute_rake(file,task)
require 'rake'
rake = Rake::Application.new
Rake.application = rake
Rake::Task.define_task(:environment)
load "#{Rails.root}/lib/tasks/#{file}"
rake[task].invoke
end
At your spec:
execute_rake("tags.rake","tags:popular")
Rake tasks are pretty hard to test. The easiest solution would be to move the logic into a method in an appropriate model. You can then test that and just call the method from the rake task.