staying DRY with custom shoulds

Recently I’ve been refactoring legacy Test::Unit code to use shoulda. Sometimes you can’t factor out repetitive stuff into a context, but you still want to be DRY. Below is an example case of refactoring into a custom should. Note that you need to use class variables, and that the self.should_* method needs to be defined before it’s actually used, since shoulda is built upon metaprogramming which gets evaluated at the class level.


...

  def self.should_request sym
    sym = sym.to_s
    context "when requesting #{sym}" do
      setup do
        get :download, {:id => @@podcast.id, :type => sym}, @@session
      end
      should "ask for a device" do
         assert_match /Please select a device to download content/,  @response.body
      end
      should "increment the message count when sent #add_to_device" do
        assert_difference 'Message.count( :conditions => "device_id = 1")', @@podcast.episodes.size do
          xhr :post, :add_to_device, {:id => 1, :type => 'all', :podcast_id => @@podcast.id}, @@session
        end
      end
    end
  end

  context "A valid account" do
    setup do
      @account = accounts(:accounts_002)
      @@session = {:account_id => @account.id}
      @account.devices << Device.find(1)
      @@podcast = podcasts(:joe_cartoon)
      @@session.merge!({:podcast_episodes => @@podcast.episodes})
    end

    should_request :all
    should_request :latest
    should_request :episode
  end

...

Shoulda talk from SF Ruby Meetup

We hosted the SF Ruby Meetup last night, which was a smashing success. It was fun to have 50+ Ruby geeks crammed into our office. :)

Here are the slides from my presentation about Shoulda.

textmate bundle for shoulda

I’m pleased to announce a textmate bundle that I assembled for the wonderfully simple shoulda testing framework.

The snippets were contributed by Dan Croak, James Golick, and Sam Livingston-Gray, and mashed up by me.

Grab the latest version here: http://macromates.com/svn/Bundles/trunk/Review/Bundles/Ruby%20Shoulda.tmbundle

a yaml_to_shoulda rake task

Inspired by err’s cryptic yaml_to_spec rake task, I wrote my own version for shoulda, based off of Jeremy Hubert’s textmate bundle It’s a bit less cryptic, and it for extra nerd points it uses recursion to allow for nested contexts. :)


desc "Converts a YAML file into a Shoulda skeleton" 
task :yaml_to_shoulda do
  require 'yaml'

  def yaml_to_context hash, indent=0
    indent1 = '  '*indent
    indent2 = '  '*(indent+1)
    hash.each_pair do |context,shoulds|
      puts indent1+"context \"#{context}\" do" 
      puts    
      shoulds.each do |should|
        yaml_to_context( should, indent+1 ) and next if should.is_a?( Hash )
        puts indent2+"should_eventually \"#{should.gsub(/^should +/,'')}\" do" 
        puts indent2+"end" 
        puts
      end
    puts indent1+"end" 
  end 

  yaml_to_context( YAML.load_file( ENV['FILE'] || !puts("Pass in FILE argument.") && exit ) )
end

Here is an example YAML file and it’s output:

This blog post:
- should mention shoulda
- should be concise
- should be written by me
- when saved as a draft:
  - should have multiple revisions
  - should not be published publicly



context "This blog post" do

  should_eventually "mention shoulda" do
  end

  should_eventually "be concise" do
  end

  should_eventually "be written by me" do
  end

  context "when saved as a draft" do

    should_eventually "have multiple revisions" do
    end

    should_eventually "not be published publicly" do
    end

  end
end

I will be adding this to my shoulda textmate bundle.

shoulda: a spec framework without the hype

This morning I just discovered Shoulda, a really sweet testing framework that gives context and behavior without the whizz-bang syntax of RSpec and test-spec. Check it out!

My only complaints so far:

  • the example code on their homepage is missing “do” keywords after some of the “should” statements. oops!
  • I can no longer run an individual test in textmate with APPLE-shift-R, as there is no literal method to run, just a block. Hopefully this will be fixed with a forthcoming shoulda textmate bundle.
actionmailer ActionView ajax alphadecimal audio autotest BDD blocks capistrano ssh ruby controller css dashboard widget delegate dog puppy naming name DRM email obfuscation exceptions factory pattern filemerge find and replace finder fink fixtures fun gem google maps helper helpers imagemagick Intertrust javascript logo math meetup model openssl OS X patch Pioneer Electronics plugin polymorphism prototype.js rails rake rmagick RSA encryption ruby script shoulda subversion SyncTV TDD testing textmate tricks unique hashes unix shell validation view yaml zebra stripes