Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing a rake task in rspec (and cucumber)

I'm new to Ruby, and I've been trying to learn Rake, RSpec, and Cucumber. I found some code that will help me test my Rake tasks, but I'm having trouble getting it to work. I was told here: http://blog.codahale.com/2007/12/20/rake-vs-rspec-fight/ to drop this:

def describe_rake_task(task_name, filename, &block)
  require "rake"

  describe "Rake task #{task_name}" do
    attr_reader :task

    before(:all) do
      @rake = Rake::Application.new
      Rake.application = @rake
      load filename
      @task = Rake::Task[task_name]
    end

    after(:all) do
      Rake.application = nil
    end

    def invoke!
      for action in task.instance_eval { @actions }
        instance_eval(&action)
      end
    end

    instance_eval(&block)
  end
end

into my spec_helper.rb file.

I've managed to take this code out and run it in my cucumber steps like this:

When /^I run the update_installers task$/ do
 @rake = Rake::Application.new
 Rake.application = @rake
 load "lib/tasks/rakefile.rb"
 @task = Rake::Task["update_installers"]

 for action in @task.instance_eval { @actions }
  instance_eval(&action)
 end

 instance_eval(&block)

 Rake.application = nil
end

but when I try to get things working in rspec, I get the following error.

ArgumentError in 'Rake task install_grapevine should install to the mygrapevine directory'

wrong number of arguments (1 for 2) /spec/spec_helper.rb: 21:in instance_eval' /spec/spec_helper.rb: 21:inblock in invoke!' /spec/spec_helper.rb: 20:in each' /spec/spec_helper.rb: 20:ininvoke!' /spec/tasks/rakefile_spec.rb:12:in `block (2 levels) in '

Unfortunately, I've got just under a week of ruby under by belt, so the metaprogramming stuff is over my head. Could anyone point me in the right direction?

like image 671
Jeff D Avatar asked Apr 10 '10 13:04

Jeff D


2 Answers

This works for me: (Rails3/ Ruby 1.9.2)

When /^the system does it's automated tasks$/ do    
  require "rake"
  @rake = Rake::Application.new
  Rake.application = @rake
  Rake.application.rake_require "tasks/cron"
  Rake::Task.define_task(:environment)
  @rake['cron'].invoke   
end

Substitute your rake task name here and also note that your require may be "lib/tasks/cron" if you don't have the lib folder in your load path.

I agree that you should only do minimal work in the Rake task and push the rest to models for ease of testing. That being said I think it's important to ensure that the code is ACTUALLY run in my cron tasks during my integration tests so I think very mild testing of the rake tasks is justified.

like image 167
Jack Kinsella Avatar answered Oct 21 '22 02:10

Jack Kinsella


Since testing rake is just too much for me, I tend to move this problem around. Whenever I find myself with a long rake task that I want to test, I create a module/class in lib/ and move all the code from the task there. This leaves the task to a single line of Ruby code, that delegates to something more testable (class, module, you name it). The only thing that remains untested is whether the rake task invokes the right line of code (and passes the right parameters), but I think that is OK.

It might be useful to tell us which is the 21nd line of your spec_helper.rb. But given that the approach you posted digs deep in rake (referring to its instance variables), I would entirely abandon it for what I suggested in the previous paragraph.

like image 34
Stefan Kanev Avatar answered Oct 21 '22 00:10

Stefan Kanev