Testing rake tasks in Rails with RSpec

by mmyoji

1 min read

Suppose you have a posts:setup task in lib/tasks/posts.rake.

Prepare shared context

# spec/support/shared_contexts/rake.rb

shared_context "rake" do
  # You define `task` inside your actual test example.
  let(:rake_task) { Rake.application[task] }

  before :all do
    Rake.application = Rake::Application.new
    Rake.application.rake_require(
      self.class.top_level_description,
      [Rails.root.join("lib", "tasks").to_s],
    )
    Rake::Task.define_task(:environment)
  end

  before do
    rake_task.reenable
  end
end

Ensure you have the following in your rails_helper.rb or spec_helper.rb, and this will automatically load the above file.

# spec/rails_helper

Dir[Rails.root.join("spec", "support", "**", "*.rb")].each { |f| require f }

Actual Test file

There are three important parts with the shared_context:

  1. Top level description
  2. task variable
  3. rake_task.invoke runs the task
# spec/lib/tasks/posts_spec.rb

require "rails_helper"

# 1. This must match the directory name
#    where you define the rake task.
#
# e.g.)
#   You want to test inside `lib/tasks/foo/bar/buz.rake`,
#   and the top level description must be `"foo/bar/buz"`.
#
describe "posts" do
  include_context "rake"

  describe "setup" do
    # 2. This must be the actual rake task name.
    let(:task) { "posts:setup" }

    it "will setup initial posts" do
      # 3. and then `rake_task.invoke` executes the task.
      expect { rake_task.invoke }.to change(Post, :count).by(3)
    end
  end
end

Thats it.