new controller examples

June 30th, 2008

There’s been a lot of discussion about clarity over DRY lately. This is something that I’ve been espousing for some time, but recent posts by Jay Fields, Mikel Lindsaar and Dan North have gotten me thinking about it again with more focus.

With this in mind, I’ve been refining the examples generated for restful controllers when you run script/generate rspec_scaffold with the rspec-rails plugin. I’ve got them now where I’m pretty happy with them, but I’m curious to hear what you think. I’m not going to tell you what I changed or what to look for, I’m just going to ask you to look it over and post your comments.

There are two listings: the generated code and the output you get from running the examples. Thanks in advance for any feedback.

Read the rest of this entry

Slides from RailsConf

June 16th, 2008

Here are the slides from my session at RailsConf on Integration Testing With RSpec’s Story Runner.

Thoughts on the Dance-Off

June 7th, 2008

In his Great Test Framework Dance-Off at RailsConf 2008, Josh Susser compared rspec with test/spec and shoulda. All in all I’d say he was very fair in his comparisons and I’d recommend checking out his slides.

There were a couple of dings Josh handed rspec and I’d like to respond to them. This is not intended to sell you on using rspec if you’re not already using it. In fact, you’ll see that I agree with a some of Josh’s criticisms.

I’d much rather see developers using frameworks like test/spec and shoulda than not using anything more expressive than test/unit out of the box, but I also cringe when I hear that someone chose a different framework for reasons that are based on inaccurate or incomplete information. I take full responsibility for that. If you’re basing decisions on inaccurate or incomplete information it’s because I haven’t made the accurate and complete information available to you. This post is one step towards addressing that problem.

Read the rest of this entry

Until sometime very soon, when you describe a module in RSpec using this syntax:

describe SomeModule do
  ...
end

RSpec implicitly includes that module in the example group. This allows you to do this:

describe CatLikeBehaviour do
  it "should say 'meow' when it greets you" do
    say_hello.should == 'meow'
  end
end

module CatLikeBehaviour do
  def say_hello
    'meow'
  end
end

As is often the case with things implicit, this actually turns out to be a problem. The problem revealed itself most notably when an RSpec user reported that a describe() method in a module he was using was conflicting with RSpec’s describe() method.

One response to that thread suggested that using describe() in a module might be too generic, but I think that really hides the point. Imagine how frustrated you would get if you had examples of a module with a current? method and we decided to add a current? method to Spec::Example::ExampleGroupMethods. You’d suddenly start seeing those examples fail with stack traces eminating from RSpec instead of your code. Not good.

And so, we are going to be removing this feature.

With the 1.1.4 release, you get a warning any time that the example calls a method on self that is part of the included module. Soon it will be removed entirely.

rails helper examples

The biggest impact of this is going to be felt in rspec-rails helper examples. There are two remedies that you have if you’ve got examples that send messages to self that should be going to another object:

1. use the new helper object provided by HelperExampleGroup

describe DateHelper do
  it "should format the date as mm/dd/yyyy" do
    helper.format_date(Date.new(2008, 5, 31)).should == '05/31/2008'
  end
end

The helper object is an instance of ActionView::Base with the named module included in it, so it has access to everything else that it should have.

2. include the module explicitly

describe DateHelper do
  include DateHelper
  it "should format the date as mm/dd/yyyy" do
    format_date(Date.new(2008, 5, 31)).should == '05/31/2008'
  end
end

My recommendation is definitely the first option as I find it more expressive.

I realize this is API changing and backward-compatibility breaking, but this is one of those cases where, at least in my view, the pain is justified by the result.

RSpec-1.1.4

May 26th, 2008

We released RSpec-1.1.4 today. It’s mostly a maintenance release but there are a few of cool new features that you may want to know about and take advantage of.

hash_including

One thing that has always been a drag is having to specify every key/value pair in a hash that is received as an argument. This is especially painful in Rails controller examples because Rails adds some data to the hash and the examples really don’t care about that extra data.

Enter hash_including().

This is a mock argument matcher that let’s you expect a hash including certain key/value pairs regardless of anything else that shows up in the hash. So instead of:

account.should_receive(:deposit).with({:amount => 37.42, :date => anything()})

you can just say:

account.should_receive(:deposit).with(hash_including(:amount => 37.42))

and keep the example focused on what you’re really interested in

Thanks to Rick DeNatale who submitted this feature request and the patch to implement it.

The heckler returns

RSpec wasn’t correctly supporting heckle for a while but the spec-heckler is back in action. For those unfamiliar, you can read about heckle at zenspider’s blog.

Here’s how you heckle your Animal model in your PetStore app:

spec spec/models/animal_spec.rb --heckle Animal

Thanks to Antti Tarvainen for resurrecting this one.

stub_model

This is for rails developers who like writing view examples with mock_model() but are sick and tired of having to stub every single attribute that gets referenced in a view.

Instead of creating a mock object like mock_model() does, stub_model() creates an instance of a real model class, but cuts off it’s connection to the database, raising an error any time it tries to connect to the database.

This is inspired by projects like unit_record and NullDB, but let’s you do things at a more granular level – allowing you to hit the db in some cases (where you think you really need it) and not in others.

Of course, you may prefer to the sort of “protection” you get from those projects, which ensure that no code touches the DB at all. If you do, have at it. This is just another option for you.

All this and more

These are just a few of the issues addressed in 1.1.4. For more information, check out the changelog and lighthouse.

RSpec at github

April 9th, 2008

After a few months of exploring git and hosting RSpec’s git repository at github, we’re happy to announce that github is now RSpec’s official home for Source Code Management.

Tracking will continue to live at the lighthouse.

We will continue to release gems to http://rubyforge.org/projects/rspec, but we will no longer be committing changes to the subversion repository there. For Rails users who are using the rspec plugins for Rails, edge rails now supports git-hosted plugins.

We’ve broken the project up into four separate repositories:

See the wikis for each repository for more information about building, installing and contributing to the project.

Welcome Pat Maddox

April 4th, 2008

I’m pleased to announce that Pat Maddox is joining the RSpec Development Team.

As you may already know, Pat has been contributing great patches and actively participating on the rspec-users and rspec-devel mailing lists for quite some time. He has demonstrated a deep understanding of Behaviour Driven Development in general, and specifically as it applies to Rails, which has certainly posed some of the more interesting questions on our mailing lists.

We are all excited to have Pat on board and look forward to his continued contribution.

limiting scope of autotest

March 4th, 2008

If you use autotest with rspec or test/unit, you’ve probably had this experience (or one like it):

You want to add some new behaviour to a model object, so you write a spec, watch it fail, make it pass, and then wait until the entire spec suite runs. Even if you’ve got a fast-running suite, this can be painful sometimes.

Wouldn’t it be great if you could limit the scope of what directories autotest observes? Well it turns out that you can! Recent releases of ZenTest include a find_directories attribute on the autotest object. Just add this to your .autotest file:

Autotest.add_hook :initialize do |at|
  unless ARGV.empty?
    at.find_directories = ARGV.dup
  end
end

and then you can say:

autotest app/models spec/models

and it will only observe those directories. This is nice and flexible, but I find that most of the time I’m wanting pairs like that: app/models and spec/models, or app/views/accounts and spec/views/accounts. In that case, I’d really like to just say:

autotest models

To accomplish that you can do this to the hook instead:

Autotest.add_hook :initialize do |at|
  unless ARGV.empty?
    at.find_directories = ["spec/#{ARGV.first}","app/#{ARGV.first}"]
  end
end

Want the best of both worlds? Try this:

Autotest.add_hook :initialize do |at|
  unless ARGV.empty?
    at.find_directories = ARGV.length == 1 ? ["spec/#{ARGV.first}","app/#{ARGV.first}"] : ARGV.dup
  end
end

The only limitation of this is that it’s based on directories, not files. Once in a while, when I’m bootstrapping a new object, I’ll keep the examples and the implementation in the same file until I’ve got things fleshed out a bit the object is ready to play nice with others. In that case, I might like to just point autotest to that one file. I started working on a patch for this for ZenTest, but I’m not sure it’s worth the extra effort. What do you think?

Regardless – happy auto-exemplifying!

RSpec-1.1.3 and ZenTest-3.9.1

February 3rd, 2008

ZenTest’s last two releases are not compatible with previous versions of RSpec. This is good news because Autotest now exposes better extension points for subclasses like those that ship with RSpec. Before, RSpec had to monkey patch Autotest to control the mappings of specs to files to run, and the list of files/directories to ignore. Now RSpec gets to use public methods (instead of instance variables) and documented hooks to do it’s work.

In the long run, this will keep things more flexible for both RSpec and ZenTest. In the short run, the catch for you is that you have to use compatible versions of RSpec and ZenTest. They are:

RSpec versionZenTest version
<= 1.1.1<= 3.7.x
1.1.23.8.x
1.1.33.9.x

RSpec: new --pattern option

January 20th, 2008

Updated on 27 May, 2008

I’m not in the habit of blogging every change we make to RSpec, but this one may change the way your suite behaves if you have not been following convention.

As of RSpec’s trunk revision 3246 there is a new command line option that lets you control the filename pattern to match. This allows you to restrict files that are loaded when running the spec command in the same way that you can with rake. It also means that helper files that you may depend on are no longer loaded implicitly.

The default is:

"spec/**/*_spec.rb".

To get rspec to behave as it did before this change, use this:

--pattern "spec/**/*.rb" 

If you prefer naming your spec files “foo_example.rb”, you can do this:

--pattern "spec/**/*_example.rb" 

You can also supply multiple patterns (comma separated):

--pattern "spec/**/*_example.rb, spec/**/*_spec.rb" 

This is a very handy way to avoid loading resource files (helpers, matchers, etc) except when you require them explicitly from other files.

This is currently only in trunk, but will be part of the next release.

RSpec-1.1.2 and ZenTest-3.8.0

January 14th, 2008

The RSpec-1.1.2 release includes changes to keep RSpec compatible with autotest in ZenTest-3.8.0. This new ZenTest release boasts an improved cascading configuration model that works well for subclasses (like those that ship with RSpec) and allows users to override the mappings of specs (or tests) to code as well as the list of files that get ignored by autotest.

To support this, Autotest now loads the following files in the following order:


Autotest
AutotestSubClass
~/.autotest
./.autotest

This allows RSpec (or any other library) to override defaults set in Autotest, and then provides users both generic (~/.autotest) and project specific (./.autotest) control over the mappings and exceptions.

How can you take advantage of this?

When autotest begins to run, it calls its :initialize hook. This hook is exposed by the add_hook method. You can use this to access the mappings and exceptions using the following methods on Autotest:


clear_mappings()
add_mapping(regexp, proc)
remove_mapping(regexp)

clear_exceptions()
add_exception(string)
remove_exception(string)

add_mapping

The add_mapping method adds a key/value pair to a hash that maps regexps to procs. Whenever autotest senses that a file is touched, it looks for the regexp that matches the file name and the runs all the files returned by the associated proc.

Imagine you’re working on a shopping cart app. You have some currency conversion behaviour in a Product model that you’d like to extract to an acts_as_currency plugin, and you want autotest to observe the process. You might add a mapping like this to .autotest:


Autotest.add_hook :initialize do |at|
  at.add_mapping(%r%^plugins/acts_as_currency/lib/.*\.rb$%) {
    at.files_matching %r%^spec/models/product_spec\.rb$% +
    at.files_matching %r%^plugins/acts_as_currency/spec/.*_spec\.rb$%
  }
end

In this case, a change to any of the files in the plugin’s lib directory would cause all the plugins specs to run, as well as the spec for the Product model.

add_exception

The add_exception method adds paths to a list of paths that Autotest ignores.

I like to run autotest in verbose mode (autotest -v) because it tells me when I change a file that it doesn’t know what to do with. The drawback is that it wants to tell me every time I commit because files in the .svn/.hg/.git directories change. So I’ve got these all listed as exceptions in my ~/.autotest file, along with assorted others:


Autotest.add_hook :initialize do |at|
  %w{.svn .hg .git}.each {|exception|at.add_exception(exception)}
end

Note that autotest compiles this list to a Regexp with no anchors, so .hgignore and .gitignore would also get ignored in this case.

Cascading config and granular control

One of the coolest changes in ZenTest-3.8.0 is that autotest loads both ~/.autotest and ./.autotest. So now you can have the hooks you like on every project (like growl notifation) all in one place and still have project specific settings.

This also allows you to set up global mappings/exceptions and modify them at the project level. See Autotest’s RDoc for more info.

Advanced Rails Recipes

January 4th, 2008

I’m pleased to announce that my contribution to Mike Clark’s new Advanced Rails Recipes book has been accepted and released. The book is available right now as a Beta PDF and includes dozens of delicious and nutritious recipes for enhancing your Rails applications and the process of developing them.

This is my first formal publication in the software world [1], and I couldn’t be more pleased than to have it be a Pragmatic Bookshelf [2] publication.

My recipe is entitled Describing Behaviour from the Outside-In With RSpec and it demonstrates the BDD approach to Rails apps starting with the view and working your way down to the controllers, models and database.

There is currently one other BDD recipe: Getting Started with BDD, which uses shoulda. There are also recipes for cooking up mocks, code coverage and html validity. And that’s just the testing related recipes.

There are also dozens of recipes dealing with UI, search, email, console, REST, db enhancements and even more general design improvements.

Like the first Rails Recipes book, this one is a must-have for any serious Rails developer who wants to take it up a notch in creating great web applications with Rails.


[1] My first publication of any kind was back around 1980 when I was a young professional magician. The book is called, simply, Coin Magic, and is a must have for any serious coin magician who wants to take it up a notch (see a trend here?) in presenting awesome feats of magic with ordinary coins. Back then I went by my first and middle name, David Arthur.

[2] The Pragmatic Programmers are also publishing my upcoming book with co-author Aslak Hellesøy, tentatively entitled Behaviour Driven Development in Ruby with RSpec.

RSpec 1.1

December 13th, 2007

The RSpec Development Team is pleased as glug (that’s kind of like punch, but more festive) to announce RSpec-1.1.0.

Thanks to all who have contributed patches over the last few months. Big thanks to Dan North and Brian Takita for their important work on this release. Dan contributed his rbehave framework which is now the Story Runner. Brian patiently did a TON of refactoring around interoperability with Test::Unit, and the result is a much cleaner RSpec core, and a clean adapter model that gets loaded when Test::Unit is on the path.

RSpec 1.1 brings four significant changes for RSpec users:

  • The RSpec Story Runner
  • Nested Example Groups
  • Support for Rails 2.0.1
  • Test::Unit interoperability

Story Runner

The RSpec Story Runner is Dan North’s rbehave framework merged into RSpec. The Story Runner is a framework for expressing high level requirements in the form of executable User Stories with Scenarios that represent Customer Acceptance Tests.

RSpec 1.1 also ships with a Ruby on Rails extension called RailsStory, which lets you write executable user stories for your rails apps as well.

Nested Example Groups

Now you can nest groups to organize things a bit better:


describe RubyDeveloper do

  before(:each) do
    @ruby_developer = RubyDeveloper.new
  end

  describe "using RSpec 1.1.0" do

    before(:each) do
      @ruby_developer.use_rspec('1.1.0')
    end

    it "should be able to nest example groups" do
      @ruby_developer.should be_able_to_nest_example_groups
    end

  end

  describe "using RSpec 1.0.1" do

    before(:each) do
      @ruby_developer.use_rspec('1.0.8')
    end

    it "should not be able to nest example groups" do
      @ruby_developer.should_not be_able_to_nest_example_groups
    end

  end

end

Running this outputs:

RubyDeveloper using RSpec 1.1.0
- should be able to nest example groups

RubyDeveloper using RSpec 1.0.8
- should not be able to nest example groups

== Support for Rails 2.0.1

gem install rails
rails myapp
ruby script/plugin install http://rspec.rubyforge.org/svn/tags/REL_1_1_0/rspec
ruby script/plugin install http://rspec.rubyforge.org/svn/tags/REL_1_1_0/rspec_on_rails
script/generate rspec

Test::Unit Interoperability

Contrary to popular belief, Spec::Rails, RSpec’s Ruby on Rails plugin, has been a Test::Unit wrapper since the the 0.7 release in November of 2006. RSpec 1.1 ups the ante though, offering a smooth transition from Test::Unit to RSpec with or without Rails:

1. Start with a TestCase:


require 'test/unit'

class TransitionTest < Test::Unit::TestCase
  def test_should_be_smooth
    transition = Transition.new(
      :from => "Test::Unit::TestCase",
      :to => "Spec::ExampleGroup" 
    )
    assert_equal "really smooth", transition.in_practice
  end
end

2. Require ‘spec’


require 'test/unit'
require 'spec'

class TransitionTest < Test::Unit::TestCase
  def test_should_be_smooth
    transition = Transition.new(
      :from => "Test::Unit::TestCase",
      :to => "Spec::ExampleGroup" 
    )
    assert_equal "really smooth", transition.in_practice
  end
end

3. Convert TestCase to ExampleGroup


require 'test/unit'
require 'spec'

describe "transitioning from TestCase to ExampleGroup" do
  def test_should_be_smooth
    transition = Transition.new(
      :from => "Test::Unit::TestCase",
      :to => "Spec::ExampleGroup" 
    )
    assert_equal "really smooth", transition.in_practice
  end
end

4. Convert test methods to examples


require 'test/unit'
require 'spec'

describe "transitioning from TestCase to ExampleGroup" do
  it "should be smooth" do
    transition = Transition.new(
      :from => "Test::Unit::TestCase",
      :to => "Spec::ExampleGroup" 
    )
    assert_equal "really smooth", transition.in_practice
  end
end

5. Convert assertions to expectations


require 'test/unit'
require 'spec'

describe "transitioning from TestCase to ExampleGroup" do
  it "should be smooth" do
    transition = Transition.new(
      :from => "Test::Unit::TestCase",
      :to => "Spec::ExampleGroup")
    transition.in_practice.should == "really smooth" 
  end
end

6. Un-require test/unit


require 'spec'

describe "transitioning from TestCase to ExampleGroup" do
  it "should be smooth" do
    transition = Transition.new(
      :from => "Test::Unit::TestCase",
      :to => "Spec::ExampleGroup" 
    )
    transition.in_practice.should == "really smooth" 
  end
end

At every one of these steps after step 2, you can run the file with the ruby command and you’ll be getting RSpec’s developer friendly output. This means that you can transition things as gradually as you like: no wholesale changes.

That’s the story. Thanks again to all who contributed and to all who continue do so.

RubyConf 2007 Talk

December 7th, 2007

The RSpec/BDD talk that Dave Astels and I presented at RubyConf 2007 has been posted for your viewing pleasure.

There are also a bunch of other RubyConf talks posted on the same site.

One correction: In our talk, I said that the role/feature/reason story format came from Mike Cohn’s book User Stories Applied. The real source was a project team at Connextra that Tim MacKinnon was a part of. Tim later joined Thoughtworks, where Dan North learned about it from Tim.

I did, in fact, learn about it from Mike Cohn, but it was at a talk he did at an Agile Conference a couple of summers back. I had also read his book, and somewhere in my head merged the talk and the book.

Nested Example Groups

November 28th, 2007

Since rspec first appeared on the scene, users have been asking for nested example groups. Well it has finally arrived. RSpec 1.1.0 will ship with support for nesting, so you’ll be able to do things like this:


describe RSpec do
  before(:each) do
    @rspec = RSpec.new
  end

  describe "at release 1.0.8" do
    before(:each) do
      @rspec.version = "1.0.8" 
    end

    it "should not support nested example groups" do
      @rspec.should_not support_nested_example_groups
    end
  end

  describe "at release 1.1.0" do
    before(:each) do
      @rspec.version = "1.1.0" 
    end

    it "should support nested example groups" do
      @rspec.should support_nested_example_groups
    end
  end
end

This will output:

RSpec at release 1.0.8
- should not support nested example groups

RSpec at release 1.1.0
- should support nested example groups

If you’re using trunk, you can do this now with revision 3009 or later.

Happy nesting!