David Chelimsky announced today that an experimental version of the story runner for rspec has been committed to the trunk. This move is clearing the path which rspec is taking towards merging the rspec and rbehave frameworks. The code example he gave for this new feature is below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# The following is based on an experimental Rails-Story adapter
# in RSpec's trunk at rev2480.
#
# You run it by standing in RAILS_ROOT and saying:
#   >ruby stories/add_person.rb
#
# Please forgive the lack of RESTful convention (get '/people/create' instead of '/people/new') -
# this works with the existing example app, which is, well, OLD.


# in RAILS_ROOT/stories/helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'spec/rails/story_adapter'

# in RAILS_ROOT/stories/add_person.rb
require File.join(File.dirname(__FILE__), "helper")

Story "Add Person", %{
  As an admin
  I want to add people to the system
  So that I show how many people use my system
}, :type => RailsStory do
  Scenario "Successfully add person" do
    Given "no people in the system" do
      Person.destroy_all
    end
    
    When "creating a new person named", "Dan" do |name|
      post "/people/create", :person => {:name => name}
    end
    
    Then "viewer should see", "/people/list" do
      follow_redirect!
      response.should render_template("people/list")
    end
    
    Then "list should include", "Dan" do
      response.should have_text(/Dan/)
    end
  end
  
  Scenario "Redirect to create form on failed create" do
    Given "no people in the system" do
      Person.destroy_all
    end
    
    When "creating a new person with no name" do
      post "/people/create", :person => {:name => nil}
    end
    
    Then "viewer should see", "/people/create" do
      assert_template "people/create"
    end
    
    Then "list should not include", "Dan" do
      response.should_not have_text(/Dan/)
    end
  end
end
Going backwards with RSpec is a pain. By backwards I mean, write the specs after the specifications (my application) already exist. It feels like I'm re-writing all the code I just wrote. I can see the value in writing the specifications as we bring more team members on board and now my mindset is such that I feel like I'm writing documentation to support them as we introduce them to the code base. The real pain started to set in when I had this small problem. The problem I'm having is that rspec is returning an error every time it encounters a method in the view that takes advantage of the polymorphic helpers AND does not pass the appropriate instance variables. Rails by default will allow you to call these methods without passing arguments to it. Here is the error:
1
2
3
4
5
6
7
8

ActionView::TemplateError in 'Edit Artist Page should render the edit artist form'
label_artist_url failed to generate from {:action=>"show", :controller=>"artists"} - you may have ambiguous routes, or you may need to supply additional parameters for this route.  content_url has the following required parameters: ["labels", :label_id, "artists", :id] - are they all satisfied?
On line #3 of app/views/artists/edit.rhtml

    1: <h1>Editing artist</h1>
    2: 
    3: <% form_tag label_artist_path, :method => :put do %>
By providing label_artist_path with its arguments this error goes away:

label_artist_path(@label, @artist)
This to me however adds unnecessary noise to the already easy clutter erb syntax and I would much rather just use this method without having to pass its arguments. There is also another option: stub out the label_artist_path method. My problem with this is that I feel like I'm not really testing my views fully and it will be hard to maintain considering I use these methods everywhere in my views. I could write a module that would include these methods, but this tends to be a big no-no in the rspec philosophy as you are hiding away some of the specifications. I'm thinking the best solution is to patch whatever the problem is in rspec, but I'm not sure where to start. I had a long discussion about this with David Chelimsky about this topic on the rspec mailing list and continued on the irc channel. I even posted a bug report on it here. We were trying to pinpoint where in rails these instance variables get sent to the helpers. He made the suggestion of adding the following code into my before method:

(class << @controller; self; end).send :include, ActionView::Helpers::UrlHelper
Which gives a completely different error:
1
2
3
4
5
6
7
8
9

ActionView::TemplateError in 'Edit Artist Page should render the edit artist form'
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.url_for
On line #3 of app/views/artists/edit.rhtml

    1: <h1>Editing artist</h1>
    2: 
    3: <% form_tag label_artist_path, :method => :put do %>
For some reason the controller is not being set and that is basically where we left off. If anyone has any solutions, please comment! I will keep this post updated.