There are several reasons why you should test your Rake tasks:
- Rake tasks are code and as such deserve testing.
- When untested Rake tasks have a tendency to become overly long and convoluted. Tests will help keep them in bay.
- As Rake tasks typically depend on your models, you (should) loose confidence in them if you don’t have tests and are attempting refactorings.
A problematic Rake task test
Here is a Rake file…
File: lib/tasks/bar_problematic.rake
namespace :foo do
desc "bake some bars"
task bake_a_problematic_bar: :environment do
puts '*' * 60
puts ' Step back: baking in action!'
puts '*' * 60
puts Bar.new.bake
puts '*' * 60
puts ' All done. Thank you for your patience.'
puts '*' * 60
end
end
…and its too simplistic spec:
File: spec/tasks/bar_rake_problematic_spec.rb
require 'spec_helper'
require 'rake'
describe 'foo namespace rake task' do
describe 'foo:bake_a_problematic_bar' do
before do
load File.expand_path("../../../lib/tasks/bar_problematic.rake", __FILE__)
Rake::Task.define_task(:environment)
end
it "should bake a bar" do
Bar.any_instance.should_receive :bake
Rake::Task["foo:bake_a_problematic_bar"].invoke
end
it "should bake a bar again" do
Bar.any_instance.should_receive :bake
Rake::Task["foo:bake_a_problematic_bar"].invoke
end
end
end
Some notable aspects of testing Rake tasks:
- Rake has to be required.
- The Rake file under test has to be manually loaded.
- In this example, the Rake task depends on the
environment
task, which is not automatically available in a spec. Since we are in rspec, the environment is already loaded and we can just defineenvironment
as an empty Rake task to make the bake task run in the test.
When run, this spec fails on the second it block… and that is not the only problem with this spec and the Rake task:
- The Rake task duplicates code to output information to the user.
- The spec “should bake a bar” will output that information when run, which clobbers the spec runners output.
- The spec “should bake a bar” again will fail, because Rake tasks are built to only execute once per process. See rake.rb. This makes sense for the normal use of Rake tasks where a task may be named as the prerequisite of another task multiple times through multiple dependencies it might have – the task only needs to run once. In our tests we have to reenable the task.
A better Rake task test
A new version of the above Rake file…
File: lib/tasks/bar.rake
class BarOutput
def self.banner text
puts '*' * 60
puts " #{text}"
puts '*' * 60
end
def self.puts string
puts string
end
end
namespace :foo do
desc "bake some bars"
task bake_a_bar: :environment do
BarOutput.banner " Step back: baking in action!"
BarOutput.puts Bar.new.bake
BarOutput.banner " All done. Thank you for your patience."
end
end
… and its spec:
File: spec/tasks/bar_rake_spec.rb
require 'spec_helper'
require 'rake'
describe 'foo namespace rake task' do
before :all do
Rake.application.rake_require "tasks/bar"
Rake::Task.define_task(:environment)
end
describe 'foo:bar' do
before do
BarOutput.stub(:banner)
BarOutput.stub(:puts)
end
let :run_rake_task do
Rake::Task["foo:bake_a_bar"].reenable
Rake.application.invoke_task "foo:bake_a_bar"
end
it "should bake a bar" do
Bar.any_instance.should_receive :bake
run_rake_task
end
it "should bake a bar again" do
Bar.any_instance.should_receive :bake
run_rake_task
end
it "should output two banners" do
BarOutput.should_receive(:banner).twice
run_rake_task
end
end
end
This spec passes just fine and does not clobber the spec output. Again, let’s look at noteworthy things:
- The output of the Rake task now goes through the
BarOutput
class. This reduces code duplication and allows for easy stubbing. There are other ways to achieve a similar effect and not clobber test output: Stub puts and print, stub on $stdout. Rake.application
has a nicer way of requiring Rake files than a simpleload
, becauserake_require
knows where Rake files live.Rake::Task["TASK"].reenable
reenables the task with name “TASK” so that it will be run again and can be called multiple times in a spec.
Here is the gist: https://gist.github.com/1764423
About the Author