Slide 1

Slide 1 text

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

Slide 52

Slide 52 text

Any questions? 52

Slide 53

Slide 53 text

Thank you! 53