---
title: Testing Rake's Integration
teaser:
tags: web,rails,testing
author: Mike Burns
published_on: 2009-03-20
---

So now you're integration testing, because that's what cool kids are doing these
days. This tests the joints of your app, making sure that the model code is
being called from the controller code which is being invoked by the user.

A good integration test, as we all know, is from the user's perspective: "I
click this", "I fill in that", etc.

But what of rake tasks? Those are integration points. If they go wrong you're
depending on cron telling you, or maybe it's a task you run once a year by hand.
Either way, you want to test it.

We already move all of the rake task into the model. This simplifies unit
testing; rake tasks are now just one method call, and that method is isolation
tested.

Here's an integration test for a rake task that sends an email with all new
users:

```ruby
require 'test_helper'

class DailyEmailReportTest < ActionController::IntegrationTest
  # In order to keep updated, I should see an email
  # of all new users' profile in the past day

  should "send an email from the rake test for the specific task" do
    old_user = Factory(:user,
                       :created_at => 2.days.ago,
                       :description => "old description")
    new_user = Factory(:user,
                       :created_at => 1.hour.ago,
                       :description => "new description")
    ActionMailer::Base.deliveries.clear

    i_call_rake_task "notifications:daily:users"

    i_see_in_email "new description"
    i_do_not_see_in_email "old description"
  end
end
```

Two test helpers are defined to help out: `#i_see_in_email` and
`#i_do_not_see_in_email`. In addition to those (they just look over
`ActionMailer::Base.deliveries`), we have `#i_call_rake_task`. That's where the
magic happens.

The first step is to override `Rake::Task#invoke_prerequisites` to avoid
reloading the environment:

```ruby
require 'rake'

# Do not re-load the environment task
class Rake::Task
  def invoke_prerequisites(task_args, invocation_chain)
    @prerequisites.reject{|n| n == "environment"}.each do |n|
      prereq = application[n, @scope]
      prereq_args = task_args.new_scope(prereq.arg_names)
      prereq.invoke_with_call_chain(prereq_args, invocation_chain)
    end
  end
end
```

Then we define the helper:

```ruby
class Test::Unit::TestCase
  def i_call_rake_task(task_name)
    # Make sure you're in the RAILS_ROOT
    oldpwd = Dir.pwd
    Dir.chdir(RAILS_ROOT)

    # Get an instance of rake
    rake_app = Rake.application
    rake_app.options.silent = true

    # Back to where you were
    Dir.chdir(oldpwd)

    rake_app.init
    rake_app.load_rakefile

    task = rake_app.tasks.detect {|t| t.name == task_name}
    assert_not_nil task, "No rake task defined: #{task_name}"
    task.reenable
    task.invoke
  end
end
```

With this in place, you can be more sure that rake tasks are integrated with
your system. Wee!

![Jumping into a pile of leaves][leaves]

[leaves]: http://images.thoughtbot.com/ui/2009-3-20-6a00d83451f25369e200e54f7707a08833-800wi.jpg
