Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DRYing up RSpec - Lightning Talk

Dan Sharp
September 26, 2013

DRYing up RSpec - Lightning Talk

Video of Presentation: http://confreaks.com/videos/2777-rockymountainruby2013-lightning-talk-drying-up-rspec

Don't Repeat Yourself (DRY) is one of the core mantra's in the programming world. This applies for our tests as well. This lightning talk presents some quick ideas on how to DRY up RSpec tests, particularly using Shared Examples.

Dan Sharp

September 26, 2013
Tweet

More Decks by Dan Sharp

Other Decks in Technology

Transcript

  1. DRYing up RSpec A brief journey into code reuse for

    tests Photo credit: http://www.wisegeek.com/what-are-the-different-types-of-desert-lizard.htm
  2. Why DRY tests? Because duplicated code makes DHH cry
 


    
 
 
 
 
 
 
 
 => DRY: https://en.wikipedia.org/wiki/Don’t_repeat_yourself
  3. Test DRYing Strategies Shared Examples! Shared Contexts! Helper Methods! Including

    Modules! Looping Constructs! Custom Matchers! Fancy Ruby Tricks
  4. DRYing Rules of Thumb Don’t DRY Too Early!! Wait for

    duplication before you undupe.! Don’t Sacrifice Readability or Understandability! Tests don’t just exercise code, they describe code/intent!! Place shared examples in spec/support. I.e:! spec/support/foo_bar_shared_examples.rb! It’s all Ruby. If you don’t like it... change it!
  5. Shared Examples are... A way to define a “test template”!

    A “storage” of a test run that is applied via another test! A good RSpec way to DRY your tests! Best used when tests have an obviously common “shape”:! Controller tests! Algorithms! Subclasses
  6. A Shared Example Example Before: Two typical, auto-generated Rails controller

    specs:
 spec/controllers/posts_controller_spec.rb spec/controllers/comments_controller_spec.rb
 
 
 
 
 
 
 require 'spec_helper'! ! describe PostsController do! ! describe "GET index" do! it "assigns all posts as @posts" do! post = Post.create! valid_attributes! get :index! assigns(:posts).should eq([post])! end! end! ! describe "GET show" do! it "assigns the requested post as @post" do! post = Post.create! valid_attributes! get :show, {:id => post.to_param}! assigns(:post).should eq(post)! end! end! end! require 'spec_helper'! ! describe CommentsController do! ! describe "GET index" do! it "assigns all comments as @comments" do! comment = Comment.create! valid_attributes! get :index! assigns(:comments).should eq([comment])! end! end! ! describe "GET show" do! it "assigns the requested comment as @comment" do! comment = Comment.create! valid_attributes! get :show, {:id => comment.to_param}! assigns(:comment).should eq(comment)! end! end! end!
  7. A Shared Example Example DRY up the common calls in

    spec/support/basic_actions.rb :
 
 def described_class_name; described_class.controller_name.singularize end! ! shared_examples "a basic INDEX action" do! let(:instance) { send(symbol) }! ! it "assigns all #{described_class_name.pluralize} as @#{described_class_name.pluralize}" do! get :index! assigns(described_class_name.pluralize.to_sym).include?(instance).should be_true! end! end # a basic controller's INDEX action! ! shared_examples "a basic SHOW action" do! let(:symbol) { described_class_name.to_sym }! let(:instance) { send(symbol) }! ! it "assigns the requested #{described_class_name} as @#{described_class_name}" do! get :show, {:id => instance.to_param}! assigns(symbol).should eq(instance)! end! end # a basic controller's SHOW action!
  8. A Shared Example Example After: Simple. Clean. DRY:
 spec/controllers/posts_controller_spec.rb spec/controllers/comments_controller_spec.rb


    
 
 
 
 
 
 require 'spec_helper'! ! describe PostsController do! ! it_should_behave_like "a basic INDEX action"! it_should_behave_like "a basic SHOW action"! ! # Additional tests could be here! ! end! ! require 'spec_helper'! ! describe CommentsController do! ! it_should_behave_like "a basic INDEX action"! it_should_behave_like "a basic SHOW action"! ! # Additional tests could be here! ! end! !
  9. Quick Tips Tip 1: Name them so they read well!

    Good:! shared_examples “a fibonacci calculator”! it_should_behave_like “a fibonacci calculator”! Bad:! shared_examples “for calculating fibonacci”! it_should_behave_like “for calculating fibonacci”
  10. Quick Tips Tip 2: Remember to use described_class! Requires that

    your spec start with: describe MyClass do! You can do Ruby “magic” on it: pluralize, constantize, singularlize, to_sym, etc. def described_class_name; described_class.controller_name.singularize end! ! shared_examples "a basic INDEX action" do! let(:symbol) { described_class_name.to_sym }! let(:instance) { send(symbol) }! let(:klass) { described_class_name.camelize.constantize }! ! it "assigns all #{described_class_name.pluralize} as @#{described_class_name.pluralize}" do! get :index! assigns(described_class_name.pluralize.to_sym).include?(instance).should be_true! end! end # a basic controller's INDEX action!
  11. Quick Tips Tip 3: Pass in variables/hash for more customization

    shared_examples "a basic INDEX action" do |redirect_location|! let(:symbol) { described_class_name.to_sym }! let(:instance) { send(symbol) }! ! it "assigns all #{described_class_name.pluralize} as @#{described_class_name.pluralize}" do! get :index! response.should redirect_to redirect_location! end! end require 'spec_helper'! ! describe PostsController do! it_should_behave_like "a basic INDEX action", root_url! end! !
  12. Quick Tips Tip 4: Four variants for a shared group!

    Includes the shared examples in the current context:! include_example “name”! matching metadata (auto-inclusion)! Includes the shared examples in a nested context! it_behaves_like “name”! it_should_behave_like “name”
  13. Quick Tips Tip 5: Go Green First!! Don’t start with

    shared_examples. Get your tests green first!! Refactor to DRY using one or more DRY techniques! Stop when green...
 and still readable/understandable.