Testing Rake Tasks
Brett Chalupa
Test Ruby PDX
March 2016
1
Slide 2
Slide 2 text
Hi, my name is Brett.
I develop software for a non-profit called charity: water.
2
Slide 3
Slide 3 text
About Brett
• Programming for 10 years
• Using Ruby for the past 5 years
• TDD-ing for the past 3 years
• Primarily using RSpec, but MiniTest
3
Slide 4
Slide 4 text
Talk Overview
• Rake explained
• Why test Rake tasks
• How to test Rake tasks
• Rails-specific examples
• Live coding TDD flow
4
Slide 5
Slide 5 text
Rake Explained
5
Slide 6
Slide 6 text
–Martin Fowler
“Rake is a build language, similar in purpose to
make and ant.”
6
http://martinfowler.com/articles/rake.html
Slide 7
Slide 7 text
7
Rake is a tool for writing and executing tasks
with Ruby and a custom DSL.
Slide 8
Slide 8 text
8
Rake tasks can depend upon and call other
Rake tasks.
Slide 9
Slide 9 text
Some Use Cases
• Compiling an application (rake compile
)
• Deploying a web app (rake deploy
)
• Running an entire test suite (rake test:all
)
• Data migrations (rake data_migrations:split_full_names
)
• Running recurring tasks (Heroku Scheduler)
9
Slide 10
Slide 10 text
Rails Uses Rake
• Managing the database (rake db:create, rake db:migrate
)
• Outputting code stats (rake stats
)
• Viewing routes (rake routes
)
• Checking app details (rake about
)
• So much more (run rake -T to see all tasks for a project)
10
Slide 11
Slide 11 text
A Simple Rake Task
11
desc 'Outputs a greeting to stdout'
task :hello do
puts 'Hello!'
end
$ rake -T
rake hello # Outputs a greeting to stdout
$ rake hello
Hello!
Output
Rakefile
Slide 12
Slide 12 text
Why Test Rake Tasks
12
Slide 13
Slide 13 text
13
For the first few years of using Ruby, I did not
test Rake tasks.
Slide 14
Slide 14 text
14
I was not even sure how to test Rake tasks.
Slide 15
Slide 15 text
15
When writing Rake tasks, I had to abandon
TDD.
Slide 16
Slide 16 text
16
Ensuring Rake tasks worked became a matter
of instead of .
Slide 17
Slide 17 text
17
I kept wondering, “what makes Rake tasks
different than any other code?”
Slide 18
Slide 18 text
18
I started testing Rake tasks, and it has led to
more confidence in creating and changing
them.
Slide 19
Slide 19 text
19
The reasons for testing Rake tasks are the
same for testing any code.
Slide 20
Slide 20 text
How to Test Rake
Tasks
20
Slide 21
Slide 21 text
21
Since Rake uses Ruby, the approach for
testing tasks is similar to testing Ruby code.
Slide 22
Slide 22 text
22
Here is what the test for the hello task from
earlier could look like.
Slide 23
Slide 23 text
Hello Task (Again)
23
desc 'Outputs a greeting to stdout'
task :hello do
puts 'Hello!'
end
$ rake -T
rake hello # Outputs a greeting to stdout
$ rake hello
Hello!
Output
Rakefile
Slide 24
Slide 24 text
24
require ‘rake'
load ‘Rakefile’
describe ‘Rake tasks' do
describe 'hello' do
it 'outputs Hello! to stdout' do
expect do
Rake::Task['hello'].invoke
end.to output("Hello!\n").to_stdout
end
end
end
Slide 25
Slide 25 text
Two Kinds of Tests
• Feature level - what is the expected outcome of the
task being run?
• Unit level - what code is being called from the task?
25
Slide 26
Slide 26 text
Feature Test Examples
• Data changes
• Output to stdout
• Operating system changes (file, processes, etc.)
26
Slide 27
Slide 27 text
Unit Test Examples
• Worker being queued
• Object instantiation and method calls
27
Slide 28
Slide 28 text
28
Sometimes both feature and unit tests do not
make sense for Rake tasks.
Slide 29
Slide 29 text
29
The hello test is a feature level spec.
Slide 30
Slide 30 text
30
hello is so simple, is a unit test needed?
Slide 31
Slide 31 text
31
Let’s tweak the hello task a little.
Slide 32
Slide 32 text
32
class Greeter
def greet
puts 'Hello!'
end
end
Slide 33
Slide 33 text
33
require_relative 'greeter'
desc 'Outputs a greeting to stdout'
task :hello do
Greeter.new.greet
end
Slide 34
Slide 34 text
34
Now Greeter is responsible for greeting,
which can be unit tested.
Slide 35
Slide 35 text
35
require ‘rake'
require_relative ‘greeter'
load ‘Rakefile’
describe 'Rakefile units' do
describe 'hello' do
it 'uses a Greeter to greet' do
expect(Greeter).to receive_message_chain(
:new,
:greet
)
Rake::Task['hello'].invoke
end
end
end
Slide 36
Slide 36 text
36
describe 'hello' do
it 'uses a Greeter to greet' do
expect(Greeter).to receive_message_chain(
:new,
:greet
)
Rake::Task['hello'].invoke
end
end
Slide 37
Slide 37 text
37
The unit test covers the implementation of the
Rake task.
The feature test covers the expected outcome
of the Rake task.
Slide 38
Slide 38 text
38
This example is a bit trivial, but it lays the
groundwork for how to test more complex
Rake tasks.
Slide 39
Slide 39 text
39
Here are some smaller bits of info to know
when testing Rake tasks.
Slide 40
Slide 40 text
40
Use the #load method to make different Rake
tasks available.
# at the top of the spec
require ‘rake’
load ‘some_other_file.rake’
Slide 41
Slide 41 text
41
Use Rake::Task[‘task_name’].reenable to
allow the Rake task to be invoked again.
Rake::Task['hello'].invoke
Rake::Task['hello'].reenable
Rake::Task['hello'].invoke
Slide 42
Slide 42 text
Testing Rake Tasks in
Rails Apps
42
Slide 43
Slide 43 text
43
Here is what it would look like to unit test an
async ActiveJob job being queued.
Slide 44
Slide 44 text
44
require 'rails_helper'
describe 'donors sync' do
describe 'donors:sync_modified_between' do
subject { Rake::Task['donors:sync_modified_between'] }
before { subject.reenable }
it 'triggers the sync job for the donors' do
expect(DonorsSyncJob).to receive(
:perform_later
).with('2015-09-17', '2015-09-18')
subject.invoke('2015-09-17', '2015-09-18')
end
end
end
Slide 45
Slide 45 text
45
it 'triggers the sync job for the donors' do
expect(DonorsSyncJob).to receive(
:perform_later
).with('2015-09-17', '2015-09-18')
subject.invoke('2015-09-17', '2015-09-18')
end
Slide 46
Slide 46 text
46
namespace :donors do
desc 'Sync donors'
task :sync_modified_between,
[:start_at, :end_at] => [:environment] do |_t, args|
DonorsSyncJob.perform_later(
args[:start_at],
args[:end_at]
)
end
end
Slide 47
Slide 47 text
47
In Rails apps, Rake tasks live in the lib/tasks
directory.
Slide 48
Slide 48 text
48
Add
Rails.application.load_tasks
to the spec/rails_helper.rb to load the Rake
tasks.
Slide 49
Slide 49 text
49
There are not much differences with Rails vs.
plain old Ruby when testing Rake tasks.
There is no need to explicitly load the Rake
files, which is nice.
Slide 50
Slide 50 text
Live coding!
50
Slide 51
Slide 51 text
51
The slides and code can be viewed here:
http://bit.ly/testing-rake-tasks