Building off of my last post on Stories, I’d like to show you some improvements I was able to make with the help of WebRat.
You can install WebRat using:
script/plugin install http://svn.eastmedia.net/public/plugins/webrat/
I got the inspiration from Ben Mabey at ‘ben.send :blog’ which illustrates an approach to stories that is unbelievably easy to pick up. He also provides source code to the example that he’s built so that you can play with it. Very cool!
I snagged that source myself and started playing with it, and added a lot of functionality of my own that I found useful. The real cool thing is that the ONLY application specific code I have is this:
# user_steps.rb steps_for(:user) do Given("that $actor is not logged in") do |actor| get "/sessions/destroy" response.should be_redirect flash[:notice].should == "You have been logged out." #fails here even though in the next request it will pass #controller.should_not be_logged_in end Then("$actor $should be logged in") do |actor, should| controller.send(shouldify(should), be_logged_in) end end # navigation_steps.rb def path_from_descriptive_name(page_name) case page_name when "home" "" when "signup" "users/new" when "login" "sessions/new" else "#{page_name}" end end
With that, and the generic application-independent helper methods that also live in my steps directory, I can write the following story:
Story: Making posts As a visitor I want to be a member So that I can make posts Scenario: Glenn creates a new account Given that Glenn is not logged in And there are some number of users And no user with login 'Glenn' exists When he goes to the signup page And fills in Login with 'Glenn' And fills in Email with 'glenn@glennfu.com' And fills in Password with 'blah' And fills in Confirm Password with 'blah' And clicks the 'Sign up' button Then there should be 1 more user And a user with login 'Glenn' should exist Scenario: Glenn logs in GivenScenario Glenn creates a new account When Glenn goes to the login page And fills in Login with 'Glenn' And fills in Password with 'blah' Then he should be logged in Scenario: A member makes a post while logged in GivenScenario Glenn logs in And there are some number of posts When he goes to the posts page And clicks on 'New post' And fills in Title with 'Woo' And fills in body with 'Stuff here' And clicks the 'Create' button Then there should be 1 more post And a post with title 'Woo' should exist Scenario: A nice lady makes a post while not logged in Given that she is not logged in Then she should not be logged in Given there are some number of posts When she goes to the posts page And clicks on 'New post' Then she should see the login page Scenario: An angry visitor should be able to see posts Given that he is not logged in When he goes to the posts page Then he better not f'ing see the login page Scenario: A visitor should not be able to edit posts GivenScenario A member makes a post while logged in And that she is not logged in When she goes to the posts page And clicks on 'Edit' Then she should see the login page
Pretty cool, huh? I was even able to add some personality with the angry visitor Scenario. Notice that I don’t have any steps written about Posts or my PostsController. I don’t need it because I have generic model steps that parse the model from the text in my PlainText instructions. It has all the basics that you need. You interact with forms and links on the top level by name, and then check the results either by text on the page or by finding models.
If you’d like to play with it yourself, I’ve zipped up my stories folder for you to play with.
It also has the old user_story.rb from the previous example. If you have any suggestions for improvements to this or ideas for something else I should do with it, please leave a comment!
My only quirk with this is that if you check the first step in the steps_for(:user)
above you’ll see I have a line commented out. It checks for controller.should_not be_logged_in
and if I uncomment it, the check fails even though the two above it pass. As you can see in the PlainText, on the very next line I call the very same check and it passes. If you could help me figure out my mistake here I’d really appreciate you leaving a comment with your thoughts!
I hope you find this useful, and if so, be sure to go toss a thank you over to Ben for the inspiration.