Mar 31, 2009

Testing Rails Authlogic with Cucumber

In early rails projects I rolled my own authentication system, later I used restful-authentication. This time I'm going to try out AuthLogic which seems to be fairly popular, cleanly packaged and actively developed. I may walk through an AuthLogic installation in a future post but today I just want to make sure it can play nicely with cucumber.

First I wrote a fairly basic login scenario. You'll notice I stuck with the declarative style in writing this and I followed the advice provided in "The RSpec Book". I use direct model access to create the registered user in the 'Given' but then use simulated browser access to do the login and verify the actions success.

Scenario: successful login
Given I am the registered user John Doe
And I am on the login page
When I login with valid credentials
Then I should be on the account page
And I should see "Login successful!"


This left me with three steps to implement:

Given /^I am the registered user (.+)$/ do |login|
params = {
"login"=> login,
"password"=>"password",
"password_confirmation"=>"password"
}
@user = User.create(params)
end

When /^I login with valid credentials$/ do
fill_in('Login', :with => @user.login)
fill_in('Password', :with => "password")
click_button("Login")
end

Then /^I should be on ([^\"]*)$/ do |page_name|
response.request.path.should == path_to(page_name)
end

All fairly straight forward. I don't expect any problems using Cucumber with Authlogic.

Mar 27, 2009

Herding Tigers

I took the time last night to skim through most of the videos from the Mountain West Ruby Conference on Confreaks. Without question my favorite in the bunch was Daniel Philpott's - "Herding Tigers - Software Development and the Art of War".

If agile project management is something that interests you this video is worth watching.

Mar 26, 2009

The Facebook UI Changes

Lots of people have been talking about the UI changes on Facebook but I wasn't immediately sure of how I felt. I was more than willing to give the new look a chance. After all the old UI really sucked.

Well, I've decided. The new UI sucks even worse!

Oddly it took this change for me to understand what I was using Facebook for. Which has primarily been to watch status changes, photo uploads and exchange private messages.

The problem is that since the change everyone has started having much longer conversations on the "what's on you mind" post and unlike wall-to-wall conversations you can't opt-out of them.

I'm stuck reading them all! It sucks!

If I want to read through my updates to see things I'm interested in I have to wade through tons and tons of spam and it's not just conversation spam it's all the noise from the hundreds of absolutely retarded facebook applications.

The signal to noise ratio has dropped to the point where I've pretty much given up.

A Project Within A Project

So I've decided that I've learned what I need to from my Cucumber experiments[1,2] and now it's time to dive in for real but to do that I need to be sure I understand my objective and of course it's complicated like an onion. So I'm going to pop the why stack a few times.

My outer goal is to provide myself with a realistic Rails project that I can apply Agile, Behavior Driven Development techniques to. I'm writing the code so I can continue my education. I'm blogging about it so that I can generate some self promotion. I may benefit from the self promotion if I choose to pursue this career path again someday.

Of course it's necessary for the inner project to have legitimate driving factors as well, otherwise I can't really practice managing an agile process. So, here's a bit of background on the goals of the inner project...

The inner project is a DIY/HOWTO blog that's also an online store. The goal is to provide quality original content. So that it drives traffic to the site. So that related merchandise is exposed to reader traffic. So that stuff can be sold. So that I can make money.

So now with the stake holder's position explained it's time to start planning the project!

Pop The Why Stack

I think one of the most important idea's in lean/agile development is the attention devoted to only doing work where it provides value. One of the best expressions of this I've seen is on Cucumber's wiki page. Basically it says...

Before doing something pop the why stack recursively until you end up with one of the following business values; protect revenue, increase revenue or manage cost.

I think that's a powerful razor to cut things with!

Mar 25, 2009

Baby Step #2 with Rails, RSpec and Cucumber

In my previous post I took my first stab at Cucumber; first writing a failing Scenario, then getting it to pass, then writing a failing Spec and getting it to pass. The scenario in the previous post was overly simple, in fact I was able to complete it without writing any code at all. My goal this time is to continue my education with a slightly more realistic scenario.

One way to attack an application is in the same order it's used. In the case of a blog the author will need to add content before the site has any value, so that seems like a reasonable starting point.

# RAILS_ROOT/features/author_publish.feature
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post
Given I am on the post creation page
When I add a new post
Then I should see a page for the new post

The narrative for this feature was easy but deciding on how to approach the first scenario wasn't. In the end after browsing around the web and reading tons of material it was another post from Ben Maybe called "Imperative vs Declarative Scenarios in User Stories" that seemed to help me decide.

With that feature in place running cucumber:
./script/cucumber features/author_publish.feature

Produces the following:
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post # features/author_publish.feature:6
Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6
Can't find mapping from "the post creation page" to a path. (RuntimeError)
/home/mgreenly/Projects/blog/features/support/paths.rb:11:in `/^I am on (.+)$/'
features/author_publish.feature:7:in `Given I am on the post creation page'
When I add a new post # features/author_publish.feature:8
Then I should see a page for the new post # features/author_publish.feature:9

1 scenario
1 failed step
2 undefined steps

You can implement step definitions for missing steps with these snippets:

When /^I add a new post$/ do
pending
end

Then /^I should see a page for the new post$/ do
pending
end

The problem here is cucumber doesn't know what to map the phrase 'the post creation page' to. This can be fixed by adding the following code to the navigation helper.
# RAILS_ROOT/features/support/paths.rb
def path_to(page_name)
case page_name

when /the homepage/
root_path

when /the post creation page/
post_path("new")

else
raise "Can't find mapping from \"#{page_name}\" to a path."
end
end

Now we run cucumber again:
./script/cucumber features/author_publish.feature

And it produces the following:
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post # features/author_publish.feature:6
Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6
undefined method `post_path' for # (NoMethodError)
/home/mgreenly/Projects/blog/features/support/paths.rb:9:in `/^I am on (.+)$/'
features/author_publish.feature:7:in `Given I am on the post creation page'
When I add a new post # features/author_publish.feature:8
Then I should see a page for the new post # features/author_publish.feature:9

1 scenario
1 failed step
2 undefined steps

You can implement step definitions for missing steps with these snippets:

When /^I add a new post$/ do
pending
end

Then /^I should see a page for the new post$/ do
pending
end

Now it's complaining that it can't find a mapping for the Post resource. That makes sense since in my previous round I only created a controller. Instead of laboring through the process to create the Post model why not cheat and use the built in generator.
./script/generate rspec_scaffold -f Post title:string body:text

Obvious, going this route means I've broken my previously completed feature, so out of curiosity lets check it out.

Running cucumber on the original feature:
./script/cucumber features/reader_browse.feature

Produces the following:
Feature: browsing
As a reader
I want to browse the site
So that I can read it's content

Scenario: visit the homepage # features/reader_browse.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93
expected the following element's content to include "Hello world!":
Posts: index
Listing posts
Title
Body
New post
(Spec::Expectations::ExpectationNotMetError)
features/reader_browse.feature:8:in `Then I should see "Hello world!"'

1 scenario
1 failed step
1 passed step


Broken as expected. The problem is that the index page no longer has the "Hello world!" text on it. No problem we'll fix that later, for now lets keep going on the new feature.

When I run cucumber now:
./script/cucumber features/author_publish.feature

I get:
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post # features/author_publish.feature:6
Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6
When I add a new post # features/author_publish.feature:8
Then I should see a page for the new post # features/author_publish.feature:9

1 scenario
2 undefined steps
1 passed step

You can implement step definitions for missing steps with these snippets:

When /^I add a new post$/ do
pending
end

Then /^I should see a page for the new post$/ do
pending
end

So now the it's obviously finding the 'the post creation page' but the remaining When/Then are still pending.

So now I add the following code:
# RAILS_ROOT/features/step_definitions/publish_steps
When /^I add a new post$/ do
fill_in "Title" , :with => "Some Title"
click_button "Create"
end


Then I run cucumber:
./script/cucumber features/author_publish.feature

And get the following:
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post # features/author_publish.feature:6
Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6
When I add a new post # features/step_definitions/posts_steps.rb:1
Then I should see a page for the new post # features/author_publish.feature:9

1 scenario
1 undefined step
2 passed steps

You can implement step definitions for missing steps with these snippets:

Then /^I should see a page for the new post$/ do
pending
end

Now I just need to satisfy the 'Then'
# RAILS_ROOT/features/step_definitions/publish_steps
Then /^I should see a page for the new post$/ do
response.should contain("Post was successfully created")
end


Then I run cucumber and get the following:
Feature: author publish
As an author
I want to publish a post
So that it can be read

Scenario: publish a post # features/author_publish.feature:6
Given I am on the post creation page # features/step_definitions/webrat_steps.rb:6
When I add a new post # features/step_definitions/publish_steps.rb:1
Then I should see a page for the new post # features/step_definitions/publish_steps.rb:6

1 scenario
3 passed steps


Everything works, except the feature I broke, but I'll pick up there in the next post.

Mar 24, 2009

The Importance of Roles in BDD

As I continue to try wrap my head around Cucumber, RSpec and BDD I found myself struggling while trying to write stories. Then I found this comment from Ben Mabey on one of Joseph Wilk's blog articles; "Telling a good story - respec stories from the trenches". It really helped to provide something I was missing, an undertstanding of how important the story tellers perspective is.

After reading that I defined a very specific set of roles (Admin, Author, Editor, Hacker and Reader) that I'll be using as I write my stories.

Mar 23, 2009

Baby Step #1 with Rails, RSpec and Cucumber

There are lots of resources on the web to help get started with RSpec and Cucumber but there's still a learning curve that I think only experience will overcome. So I decided just to dive in. I don't claim what I've done here is right, or even a good idea, but it's where I started.

My initial goal is just to walk through the 'outside in' red/green/refactor process of writing a feature then examples without spending to much time worrying if I selected the right story or if it's implemented it correctly.

This initial cycle is mostly about learning what the tools can do for me.

The simplest story that I can imagine goes like this:

as a reader I want to browse the site so that I can read it's content.


So with that story in hand It's time to write my first feature
 #  ./features/reader_browses.feature
Feature: browsing
As a reader
I want to browse the site
So that I can view it's content

Scenario: visit the homepage
When I go to the homepage
Then I should see "Hello world!"


Next I run Cucumber, 'rake features', and get the following....

 Feature: browsing
As a reader
I want to browse the site
So that I can view it's content

Scenario: visit the homepage # features/reader_browses.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
undefined method `root_path' for # (NoMethodError)
/home/mgreenly/Projects/blog/features/support/paths.rb:6:in `/^I go to (.+)$/'
features/reader_browses.feature:7:in `When I go to the homepage'
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93

1 scenario
1 failed step
1 skipped step
rake aborted!


So the first error I get is because we don't have a default route, so lets fix it by adding the following line in "./config/routes.rb"

map.root :controller => 'posts'


Then I run Cucumber again, "rake features", and get the following....

 Feature: browsing
As a reader
I want to browse the site
So that I can read it's content

Scenario: visit the homepage # features/reader_browse.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
uninitialized constant PostsController (NameError)
(eval):2:in `/^I go to (.+)$/'
features/reader_browse.feature:7:in `When I go to the homepage'
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93

1 scenario
1 failed step
1 skipped step
rake aborted!


So now it's telling me that the default route points to a PostsController and that controller doesn't exist. So lets create both the controller and it's rspec framework with the built in generator.

./script/generate rspec_controller posts


Now I'll run the spec, "rake spec", to see what I get
 ..

Finished in 0.045209 seconds

2 examples, 0 failures


It passes of course because the controller doesn't do anything and the spec doesn't make any requirements on it.

So lets see what Cucumber says now, "rake features"
 Feature: browsing
As a reader
I want to browse the site
So that I can view it's content

Scenario: visit the homepage # features/reader_browses.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
No action responded to index. Actions: (ActionController::UnknownAction)
/usr/local/stow/ruby-1.8.7-p72/lib/ruby/1.8/benchmark.rb:308:in `realtime'
(eval):2:in `/^I go to (.+)$/'
features/reader_browses.feature:7:in `When I go to the homepage'
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93

1 scenario
1 failed step
1 skipped step
rake aborted!


There's still an error but it's changed. Now it's complaining because the there's no 'index' action on the Posts controller. So lets drop back to the rspec example for the Posts controller and create a requirement for the index action. Remember first I want it to fail, then pass.

# in ./spec/controllers/posts_controller_spec.rb
describe "GET 'index'" do
it "should be successful" do
get 'index'
response.should be_success
end
end


So now when I run "rake spec" I get....

.F.

1)
ActionController::UnknownAction in 'PostsController GET 'index' should be successful'
No action responded to index. Actions:
./spec/controllers/posts_controller_spec.rb:12:

Finished in 0.052123 seconds

3 examples, 1 failure
rake aborted!


Now that the spec is failing we can try to satisfy it. To do that I just create an empty file at "app/views/posts/index.erb" and then rerun "rake spec"
 ...

Finished in 0.049891 seconds

3 examples, 0 failures


So now that the spec passes I run Cucumber again, "rake features", and get ....
 Feature: browsing
As a reader
I want to browse the site
So that I can view it's content

Scenario: visit the homepage # features/reader_browses.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93
Could not parse document (RuntimeError)
features/reader_browses.feature:8:in `Then I should see "Hello world!"'

1 scenario
1 failed step
1 passed step
rake aborted!



It's still failing but now it's upset because it can't find the "Hello world!" text on the index page. This is easy to fix, I just need to add the text to "app/views/posts/index.erb". Now I run "rake features".....

Feature: browsing
As a reader
I want to browse the site
So that I can view it's content

Scenario: visit the homepage # features/reader_browses.feature:6
When I go to the homepage # features/step_definitions/webrat_steps.rb:10
Then I should see "Hello world!" # features/step_definitions/webrat_steps.rb:93

1 scenario
2 passed steps

It passes. Just to make sure I didn't screw anything up I run "rake spec" on more time.
...
Finished in 0.049713 seconds
3 examples, 0 failures


That makes one successfull pass through an outside-in red/green/refactor cycle using Cucumber and Rspec with Rails.

Start a BDD Rails Project

I was starting a new rails project today that I plan on developing with Cucumber and RSpec. I quickly realized that even though there are tons of tutorials out there none seemed to really describe the process of initializing a new project. So here it is in a nutshell.

$ rails blog
$ cd blog
$ ./script/generate rspec
$ ./script/generate cucumber
$ rake db:migrate

At this point you should be able to do a 'rake', 'rake spec' or 'rake features' command and not get any errors.

Rails Development On Ubuntu

There's no shortage of posts on the web describing how to setup Ubuntu for Rails development but I'm going to add one more anyway. I choose to write this because none of the posts I ran across were geared towards a current Rails, RSpec, Cucumber, Git setup.

I'm using Ubuntu 9.04 (JauntyJackalope) during this installation.

First install Ruby and it's associated packages.

sudo apt-get install ruby-full

Next install the native SQlite3 libraries and command line tools.
sudo apt-get install sqlite3 libsqlite3-dev

Next install some native xml processing libraries used by webrat.
sudo apt-get install libxml2-dev libxslt-dev

Next install Git
sudo apt-get install git-core

Next install the tools necessary to build native rubygem extensions.
sudo apt-get install ruby-dev build-essential

Next install RubyGems from source because the Ubuntu packages are usually not current. I also prefer to install RubyGems into my $HOME directory because mixing source packages in with distribution managed packages is generally a bad idea.
wget -c http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz
tar xvzf rubygems-1.3.1
cd rubygems-1.3.1
ruby setup.rb --prefix=$HOME/.rubygems --no-format-executable

Once that's done you need to set some environment variables so that; Ruby can find the gems library, RubyGems can find the gem repository and executable gems will be in the $PATH. Add the following to the file $HOME/.bashrc
export RUBYLIB=$HOME/.rubygems/lib
export GEM_HOME=$HOME/.rubygems/gems
export PATH=$HOME/.rubygems/bin:$GEM_HOME/bin:$PATH

Then make sure the RubyGems library is up-to-date and install all the necessary gems.
gem update --system
gem install rake rails rspec-rails cucumber webrat sqlite3-ruby

If you want to double check your installation you can try the following...
ruby --version
gem --version
rails --version
spec --version
cucumber --version

RubyGems Documentation Index

In case there's anyone out there that isn't aware; running RubyGems server provides browseable documentation at http://localhost:8808/ for all the installed gems that have rdocs. This morning I decided to automate the launching of the gem server at startup by adding it to my bash profile.

# in $HOME/.profile
gem server --daemon

If you've previously installed some gems without documentation you can regenerate them this way.
gem rdoc --all