Generating Multiple RSpec Files from a Design Document

Ruby, Software Tweaking, Test Driven Development, YAML — Tags: , , , , — Ardekantur @ 10:55 pm

Taking queues from Adam Wiggin’s first commit to his Rush project, simply a design document and some specs, and Err the Blog’s post about BDD and generating specs from YAML, I’ve written a small Rake task that will take a single file design document in YAML and separate it out into the components you specify. For example:

spec/design.yml

Website Integration:
    - should be able to log in from the front page and land at your profile
 
OpenID module:
    - should accept an OpenID URL
    - should return an error message if the URL cannot be reached
 
User:
    - should have a valid user name
    - should return an error if creating a duplicate user name is attempted

And then:

rake design

Takes the section headers, and flattens them as the filenames. So this particular design creates three different files, website_integration_spec.rb, openid_module_spec.rb, and user_spec.rb. They’ll look like this:

spec/user_spec.rb

require File.dirname(__FILE__) + '/spec_helper'
 
describe 'User' do
 
  it 'should have a valid user name'
  it 'should return an error if creating a duplicate user name is attempted'
 
end

And just like that, you have your specs, ready to go!

Here it is:

Please note that it will destroy those files if they already exist, replacing them! I really don’t care if you lose your existing specs! Back them up if you want to try this! You have been warned! This is more of a technique for a brand new project, anyway.

Also, please note that I’ve embedded the rake task as a GitHub Gist paste, so feel free to fork it and modify it. I’d be interested in seeing cleaner, more interesting implementations of my 30-second hack. Additionally, please choose view raw if you want to copy it, seeing as how GitHub seems to bork on a newline character in the code snippet.

Using Gist for Responsible Software Blogging

Interesting Links — Tags: , , , — Ardekantur @ 9:16 pm

GitHub, purveyors of high-quality source control beauty, released Gist just the other day. Gist, in a nutshell, is pasteable texts controlled by git. Every single one has its own repository. So in theory you can download them, fork them, and thanks to the GitHub guys, embed them in your own webpages. This provides a unique opportunity to ensure that the posts you write about edge community stuff — I’m looking at you, Rails and Merb jerks :-) — can remain relevant. Simply write a Gist with your source code and corresponding output, and embed that in your blog. Tag that blog entry somehow with a reminder to check it every four months or something. And, every four months, when something reminds you, you take another look at the source code snippet, and make sure it’s relevant.

What needs fixing with Gist?

  • Downloading snippets at all doesn’t seem to work. It makes sense to not have that button when it comes to snippets comprised of a single file, but with being able to specify multiple files for a single snippet, downloading should archive and download them all.
  • It should be possible to only embed one part of a snippet, instead of all parts of them, in the case when multiple files are specified.

What would be awesome?

  • Being able to specify a certain file of the snippet as an rspec or Test::Unit file for the snippet. This way, we can have auto-testing snippets. This is clearly in a fantastical sort of realm, but who knows what will be possible when Gist becomes more robust?

Page Searching in Merb Specs with Hpricot

Merb — Tags: , , , , — Ardekantur @ 9:29 pm

I’m not entirely sure what the status of this code is, but it seems to be working for me. I tried throwing it a little further upstream and got hit with 42 failed specs, so I’m content to use it as a spec helper for now.

In any event, yeah. There’s Hpricot goodness available for your Merb specs, it just has to be enabled. Where normally we’d get a controller response with a string containing the HTML of the body, we’ll get an Hpricot document that’s easily searchable for tags and such.

Here’s the helper:

spec/spec_helper.rb

  def dispatch controller, action = :index, opts = {}
    @controller = dispatch_to(controller, action, opts)
    @controller.body = Merb::Test::ViewHelper::DocumentOutput.new @controller.body
  end

Now in any of your specs, do this:

  describe Main, "index action" do
 
    it "should have a title of 'Happy Days Are Here'" do
      dispatch Main, :index
      @controller.body['title'].inner_text.should == "Happy Days Are Here"
    end
  end

Tada! No parsing through the document looking for the title tag. Very nice.

The methods you get are these:

  • []: The index method will return a single element if there’s one of them in the document, or an Array of them if there’s multiple ones. Then you can pick out the data you want.
  • content_for: This method takes an HTML element or HTML element and class specification (like ‘div’ ‘div.content’) and returns the text contents of the first found tag that matches that element or element and class. This means if you have code like this:
  <div class='awesome'>
    This is some content.
    <b>This is some more content.</b>
    <a href='http://google.com'>This is a link.</a>
    <ul>
      <li>Item</li>
      <li>Item</li>
    </ul>
  </div>

Then calling @controller.body.content_for('div.awesome') will return the literal interpretation of all of the text, indentations and all, like so:

    This is some content.
            This is some more content.
            This is a link.
            Item
              Item

So be very careful doing matches with this method!

  • content_for_all: This method is the same as content_for, except it will parse through the entire document and find all of the elements that match that tag or tag and class, and return them in an Array. If you’re only looking for one instance of a tag, use content_for, as this will be needlessly time-consuming.

Edit: I should also mention that the [] method takes XPath expressions, so you could do something like div[@class='awesome']/ul/li to get an Array of the list items in the last example. Sweet!

RSpec, Autotest, and Gnome-Notify

Configuration Files, Software Tweaking — Tags: , , , — Ardekantur @ 9:29 pm

I’ve taken coldfire’s excellent .autotest and modified it to provide a third state for rspec tests that are still pending.

# vi: ft=ruby
 
require 'autotest/redgreen'
require 'autotest/timestamp'
 
module Autotest::GnomeNotify
  def self.notify title, msg, img
    system "notify-send '#{title}' '#{msg}' -i #{img} -t 1000"
  end
 
  Autotest.add_hook :ran_command do |at|
    image_root = "~/.autotest_images" 
    results = [at.results].flatten.join("\n")
    results.gsub!(/\\e\[\d+m/,'')
    output = results.slice(/(\d+)\sexamples?,\s(\d+)\sfailures?(,\s(\d+)\spending)*/)
    puts output.inspect
    if output
      if $~[2].to_i > 0
        notify "FAIL", "#{output}", "#{image_root}/fail.png"
      else
        if $~[4].to_i > 0
          notify "Pending", "#{output}", "#{image_root}/pending.png"
        else
          notify "Pass", "#{output}", "#{image_root}/pass.png" 
        end
      end
    end
  end
 
end

Ruby, RSpec and BDD Part I

Ruby, Tutorials — Tags: , , , — Ardekantur @ 7:10 pm

Here is a bit of work on Behavior Driven Development (BDD) with rspec in Ruby, based off the examples provided by this excellent Null Is Love series of posts. The only thing I know externally, in terms of BDD, is the process itself:

  1. Write a test for the functionality you want.
  2. Watch the test fail.
  3. Write code to support that functionality.
  4. Watch the test pass, or watch it fail, and fix your code.
  5. Repeat incrementally.

This post assumes that you’ve read and gone through the series of posts on Null Is Love. Gain an understanding of unit testing with those posts, and then come back here.

With that in mind, let’s start.

$ sudo gem install rspec -y
$ mkdir car
$ touch car/car_spec.rb car/car.rb

This is the gem we need, rspec, followed by the files we will be working with. So let’s start with the first leg of this - writing the functionality we want. Initially we will just want our Car object to initialize with the proper default values. We won’t be using all of the specification funcationality of rspec—just using it to write cleaner, english-language tests is enough for now.

car/car_spec.rb

require 'car'
 
describe Car do
 
  it "should initialize with the proper defaults" do
    default_car = Car.new
    default_car.model.should == 'Volvo'
    default_car.year.should  == 2007
    default_car.color.should == 'unknown'
  end 
 
end

The first thing you may notice is that this code is very clean, and extremely readable. It also does not try to over-extend itself. We are testing one small portion of our eventual code’s functionality.

Now we test our code.

$ spec -c car_spec.rb
./car_spec.rb:3: uninitialized constant Car (NameError)

Whoops! That’s right, we haven’t written any code yet. Our test will fail. This is what we want. If the tests passed immediately, we wouldn’t need to write them at all. This is a rather roundabout way of thinking about what we are doing, but it works. (NOTE: I am using the ‘-c’ parameter with spec in order to see colored output. This may or may not work on whatever terminal or terminal emulator you are using, but it is a wonderful way to feel better about your code when you start the green that lets you know your tests have passed.)

Let us start writing our code, then.

car/car.rb

class Car
  attr_accessor :model, :year, :color
 
  def initialize( options = {} )
    self.model = options[:model] || 'Volvo'
    self.year  = options[:year]  || 2007
    self.color = options[:color] || 'unknown'
  end
 
end

And now:

$ spec -c car_spec.rb 
.

Finished in 0.016665 seconds

1 example, 0 failures

Beautiful! We’re green.

Here are some other tests that reflect the concerns of the original design. Each of them requires no changes to the object code in order to pass.

  it "should use values provided as parameters to the constructor" do                                       
    custom_car = Car.new :model => 'Toyota', :year => 2005, :color => 'white'                               
    custom_car.model.should == 'Toyota'                                                                     
    custom_car.year.should  == 2005                                                                         
    custom_car.color.should == 'white'                                                                      
  end                                                                                                       
 
  it "should accept constructor parameters in any order" do                                                 
    shuffled_car = Car.new :color => 'red', :model => 'Kia', :year => 2008                                  
    shuffled_car.model.should == 'Kia'                                                                      
    shuffled_car.year.should  == 2008
    shuffled_car.color.should == 'red'
  end

Remember the part of Null is Love’s tutorial where we asked ourselves what the behavior of a certain accessor—namely, the year—should be?

Do you change your test or change your code? If you need to rely on string_car.year always being an integer, then I would suggest changing your code. If you want string_car.year to be able to accept either a string or an integer, then you change your test assertion so that “a string in results in a string out”. In most cases, it will probably the former.

With BDD, that question is asked, but in a different way. As we spec out our code, we specify what the behavior should be before we write the code that deals with it. In this case, the code will be changed to reflect the behavior we want, but that requirement is a sort of first class citizen, instead of an afterthought of going through the code, checking for gotchas.

car/car_spec.rb

    # ...
 
    it "should ensure years are always integers" do                                                           
      old_car = Car.new :year => '1960'                                                                       
      old_car.year.should == 1960                                                                             
    end
 
    #...

And the results of our test?

$ spec -c car_spec.rb 
...F

1)
'Car should ensure years are always integers' FAILED
expected: 1960,
     got: "1960" (using ==)
./car_spec.rb:28:

Finished in 0.017983 seconds

4 examples, 1 failure

Perfect! Now we write the code that lets our test pass—namely, converting the options[:year] parameter to an integer when it is passed.

I’ll continue this tutorial eventually, along with some additional information about heckling our code.

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
(c) 2008 Ardekantur | powered by WordPress with Barecity