A Custom RSpec Example Group for Testing Rake Tasks
As I continue to strive to be better at test-driven development (BDD for you pursists), I'm continually surprised by how much friction there is within the testing frameworks for testing some of the dustier corners of the Rails stack. One of these overlooked parts is Rake tasks. While it could be argued that they aren't as important to test as customer-facing pages, Rake tasks can do a lot of the heavy lifting in some applications, and merit testing to ensure that the critical back-end functionality is behaving as you would expect.
Since there isn't anything included with RSpec for testing Rake tasks, I took the time earlier this year while working on Top Ruby Jobs to write a custom example group to facilitate such testing.
class RakeExampleGroup < Spec::ExampleGroup Spec::Example::ExampleGroupFactory.register(:tasks, self) require 'rake' set_description "Rake task" let(:rake) { Rake::Application.new } let(:task) { rake[description_args.first] } let(:namespace) { description_args.first.split(':').first } before(:each) do Rake.application = rake Rake.application.rake_require(File.join('lib', 'tasks', namespace)) Rake::Task.define_task(:environment) end after(:each) do Rake.application = nil end end
Any specs under the /spec/tasks
path will use this example group. Here's an example.
require 'spec_helper' describe 'content:sync' do it "has 'environment' as a prerequisite" do task.prerequisites.should include('environment') end it "syncs the remote content" do Resque.should_receive(:enqueue).with(SyncContentJob) task.invoke end end
This looks for the file /lib/tasks/content.rake
, and calling task.invoke
will run the task content:sync
just like running rake content:sync
on the command line would.
Unfortunately, this example group doesn't work in RSpec 2, which has undergone significant API changes. It took me a little while to figure out how example groups work in RSpec 2 in order to get my RakeExampleGroup
working there, but here's what I came up with.
module RakeExampleGroup extend ActiveSupport::Concern included do require 'rake' metadata[:type] = :task let(:rake) { Rake::Application.new } let(:task) { rake[self.class.description] } let(:namespace) { self.class.description.split(':').first } before do Rake.application = rake Rake.application.rake_require(File.join('lib', 'tasks', namespace)) Rake::Task.define_task(:environment) end after do Rake.application = nil end end RSpec.configure do |config| config.include self, :example_group => { :file_path => %r(spec/tasks) } end end
I'm including both versions here, because I wasn't able to find any advice on how to migrate custom example groups to Rspec 2, so hopefully this proves helpful to someone else. It should be noted, though, that this particular example does depend on rspec-rails
, which is fine in my case since I'm working within the context of a Rails application, but the include_self_when_dir_matches
method comes from RSpec::Rails::ModuleInclusion
, so you'd have handle that magic yourself if you're not using it within Rails.
Updated the RSpec 2 example to work with newer versions thanks to feedback from @jnimety.