Testing rake tasks with rspec

Testing rake tasks with rspec

This blog post is a continuation of this thread.

So for example

# ./lib/tasks/foo_task.rake
desc 'Foo task'

namespace :task do
  task :my_task, [:foo, :bar] => [:baz] do |task, args|
    ...
    # does my_task
    ...
  end
end

Now if we try writing a spec a for it

# ./spec/tasks/foo_task_spec.rb
require 'rails_helper'

Rails.application.load_tasks

describe "task_my_task" do
  context "foo case" do
    let(:arg1) {"foo"}
    let(:arg2) {"baz"}

    it "it does foo behaviour" do
      Rake::Task["task:my_task"].invoke(arg1, arg2)

      # assert the expected behaviour here related for foo case
    end
  end

  context "baz case" do
    let(:arg1) {"bazbee"}
    let(:arg2) {"foobee"}

    it "it does baz behaviour" do
      Rake::Task["task:my_task"].invoke(arg1, arg2)

      # assert the expected behaviour here related for baz case
    end
  end
end

Now if we try to run the specs for specific contexts, where the rake task is being invoked, all will work well, but when we try to run the specs for all the contexts in the test file, the first rake task will run, but the rest of them will start failing.

Which is confusing, turns out that the tasks can be invoked only once in a given context. Not sure of the history behind this or the reasoning on why this is the case, couldn’t find it. (let me know if you were able to get this bit, would be happy to learn it’s history)

How to make it work

To work around this, the task needs to be explicitly re-enabled in before the next spec is run.

Addin an after_each block would work for starters, if inside that block you would re-enable your task which you are trying to test out in your spec, which would mean that after each spec, inside which you are exercising your method, this routine is called. So something like

# ./spec/tasks/foo_task_spec.rb
require 'rails_helper'

Rails.application.load_tasks

describe "task_my_task" do
  after(:each) do
    Rake::Task["task:my_task"].reenable
  end

  context "foo case" do
    let(:arg1) {"foo"}
    let(:arg2) {"baz"}

    it "it does foo behaviour" do
      Rake::Task["task:my_task"].invoke(arg1, arg2)

      # assert the expected behaviour here related for foo case
    end
  end

  context "baz case" do
    let(:arg1) {"bazbee"}
    let(:arg2) {"foobee"}

    it "it does baz behaviour" do
      Rake::Task["task:my_task"].invoke(arg1, arg2)

      # assert the expected behaviour here related for baz case
    end
  end
end

reenable first resets the task’s already_invoked state, allowing the task to then be executed again with all it’s dependencies.

There are execute and invoke methods too which are mentioned in this SO answer. No preference as such to why I went with reenable.

Running all the specs would work now.

References

Credits