I’m trying to write tests in rspec for two rake tasks which are defined in the same file (in a Rails 3.0.11 project). For some reason only one of them passes. I’ve written a small demo to abstract away the actual content of the tasks, and the same thing happens. Both tasks work when invoked with rake from the command line. What’s going on? Here’s my demo:
lib/tasks/demo_tasks.rake
namespace :demo do
task :test => :environment do
puts "test!"
end
task :test_two => :environment do
puts "second test!"
end
end
spec/lib/tasks/demo_spec.rb
require 'spec_helper'
require 'rake'
describe "test tasks" do
let(:rake) do
app = Rake::Application.new
app.options.silent = true
app
end
before :each do
Rake.application = rake
Rake.application.rake_require 'lib/tasks/demo_tasks',
[Rails.root.to_s]
Rake::Task.define_task :environment
end
describe "demo:test" do
it "runs" do
rake["demo:test"].invoke
end
end
describe "demo:test_two" do
it "also_runs" do
rake["demo:test_two"].invoke
end
end
end
rspec spec/lib/tasks/demo_spec.rb
test tasks
demo:test
test!
runs
demo:test_two
also_runs (FAILED - 1)
Failures:
1) test tasks demo:test_two also_runs
Failure/Error: rake["demo:test_two"].invoke
RuntimeError:
Don't know how to build task 'demo:test_two'
# ./spec/lib/tasks/demo_spec.rb:26:in `block (3 levels) in <top (required)>'
Nutshell: Change your
beforeto abefore :all(instead of:each).Or: Pass an empty array as a third parameter to
rake_require.Details
$"is a Ruby special variable that holds an array of modules loaded byrequire.If you don’t pass the optional parameter,
rake_requirewill use that array of modules loaded by Ruby. This means the module won’t be loaded again: Ruby knows the module was loaded, rake checks to see what Ruby knows, and it’s a new rake instance for each test.Switching to
before :allworked because it meant theletblock only ran once: one rake instance, one module load, everybody is happy.All this said, why reload the rake environment twice anyway? Your goal is to test your tasks, which doesn’t require a fresh rake context for every spec.
You could eliminate the local altogether at the cost of some minor verbosity in each spec:
You could define an instance variable in the
beforeblock to avoid theRake::Taskreference:IMO, less desirable for a number of reasons. Here’s a summary I agree with.