It’s tricky to test rake tasks for following reasons.

  1. It’s annoying to see the task’s outputs when running tests if you set it up so.
  2. rake does not allow you to run the same task for multiple test examples as it is.

First you want to create a tag so that you can apply the same setup for all the rake task tests. Here I named it ‘rake_test’.

Here’s a sample of such cases with Ruby on Rails.

# spec_helper.rb
config.before :all, rake_test: true do
  stream = File.open(File::NULL, 'w')
  @original_stdout = stdout.clone
  @original_stderr = $stderr.clone
  $stdout = $stderr = stream
  Rake::Task.define_task(:environment)
end

config.after :all, rake_test: true do
  $stdout = @original_stdout
  $stderr = @original_stderr
end

It suppresses unnecessary outputs from the tasks when you run RSpec. File::NULL usually goes to /dev/null. We keep the original $stdout and $stderr as instance variables here, so that we can put them back later. If, however, you are sure original $stdout is always STDOUT and $stderr is STDERR, the code above can be shorter.

# spec_helper.rb
config.before :all, rake_test: true do
  stream = File.open(File::NULL, 'w')
  $stdout = $stderr = stream
  Rake::Task.define_task(:environment)
end

config.after :all, rake_test: true do
  $stdout = STDOUT 
  $stderr = STDERR 
end

“define_task” is necessary to load Rails environment which is “test” in this case. And each test should be like so:

# spec/lib/tasks/your_task_spec.rb
RSpec.describe 'your:task_name', rake_test: true do
  let(:run_task) do
    Rake::Task['your:task_name'].reenable
    Rake.application.invoke_task 'your:task_name'
  end
  
  it 'does the job' do
    expect { run_task }.to change { Something }.by(expected_number)
  end
end

“invoke_task” runs your rake task as its name claims. “reenable” allows the task to be executed again if the task is invoked again.

Another note is that these tests run basically in the same process as other types of tests whereas rake tasks run in a separated process in daily operatioins. So if you overwrite something in rake task tests, you have to put the original back at the end.