Posts Tagged ‘development’

LSRC 2010 – Day 2

August 28th, 2010

Below are my notes from the second day at LSRC.

Faster Test Suites

Nick Gauthier gave an impressive talk on reducing the execution time of a test suite.  The original suite took over 15 minutes to run.  Using factory_girl, shoulda and paperclip, he strongly recommended empty DB testing (no fixtures, all tests setup and tear down their objects/records), and the use of “no side-effect” test blocks.

Switching to the use of fast_context the execution time was brought down to 5 minutes.  Using perf_tools he found that calls to ImageMagic (from Paperclip) were calling out to the shell, taking a lot of time.  So he mocked those calls and brought it down to 3 minutes.

Using Hydra to do multi-core (concurrent) testing he brought execution time down to 1 minute.

Hydra is easy to set-up (no sockets or daemons to configure).  Just add a Rake task and configuration yml and you’re ready to use it.

When you run Test::Unit the Rails environment is loaded 4 times!!  Cucumber loads the environment twice.  RSpec and Hydra load it just once.

Further tweaking EXT4 journal_data_writeback and atime (access time) on the file system he got testing down to just 50 seconds.  Using Ruby Enterprise Edition (with the tmalloc) and more tweaks (this time to the Ruby _HEAP and _GC settings) he had tests running in 18 seconds.  A 4417% increase in performance!!

Check the comments below for a link to Nick’s presentation slides.  He’s been kind enough to provide the link!

Searchability

Luigi Montanez gave an insightful presentation on SEO.  He doesn’t believe that you have to be an “expert” in SEO.  You simply need to have good content and Google (or Bing) will handle the rest.  There are some things you can do though to make sure you don’t sabotage your pages when crawlers visit them.

Make sure you have a sitemap.xml (sitemaps.org) and a robots.txt (robotstxt.org).  These help crawlers know what to pay attention to and what to ignore.  Make sure you use either a www or non-www domain.  If you have both choose one and redirect to it.  You don’t want to have the site indexed under both.

When redirecting make sure you use 302 and 301 appropriately.  302 is for a temporary redirect, and Google/Bing will not index the page it’s redirected to.  Use 301 instead, if your intention is to have the page permanently moved.  Be cautious too because many web frameworks will use a 302 by default, instead of a 301.

Do not change your content by region, nor require cookies for someone to view content.  Also page titles are very important.  Always follow the “Page Title | Site Title” style (or you can replace | with a – or some other delimiter, just be consistent).

In your URLs dashes (-) are word separators, underscores (_) are not!  Make sure you are using dashes in your URLs!!  Take advantage of meta tagging, and Google Webmaster Tools.

Takeaway:

  • Think like a searcher (search engine: crawler, index, rank)
  • Optimize your title tags (they are the most important thing)
  • Use Google Webmaster Tools (it provides valuable insight)

Components

Nick Sutterer gave a cool demonstration of Cells and Apotomo.  They allow you to create stateful widgets in your Rails views.

Padrino

Joshua Hull gave a talk on Padrino, another web framework built on top of Sinatra.  It takes a minimalist style approach (take what you need).  It’s light-weight and fast.  It has all the basic stuff you’d expect of  web framework including a simple mailer.

LSRC 2010 – Day 1

August 27th, 2010

Had a great info-filled day today at LSRC.  It’s my 3rd year attending.  Below are some of the highlights of the different talks I attended:

Real Software Engineering

Glenn Vanderburg gave a great opening talk about software engineering.  he said software engineering doesn’t work.  He explores the roots of the idea of “software engineering” all the back to the first NATO Software Engineering Conference on the subject.  At the conference they described best practices as:

  1. A software system is best designed if tests are interlaced with the software as it’s developed, rather than at the end.
  2. Creates a simulation which matches the requirements, contains the control which organizes the design of the system.
  3. Through successive repetitions of testing and design, the model ultimately becomes the software system itself.

Sound familiar?  He then talks about what went wrong, how did we start with what sounds like Agile and ended up with waterfall.  He talked about how the creator of waterfall process (Winston W. Royce) actually was trying to express he didn’t feel it worked.  His paper was so poorly written though that people (paying attention mostly to the fancy diagrams, and not the text) believed he was in favor of it.

Cost is *always* an object, and engineering is about what you can do with a dollar vs. what a “bumbler” can do with two.  Mathematical models are not supposed to be about correctness, but about saving on costs.  In traditional engineering you create models because it’s expensive to use up materials, labor, etc.  In software these things are extremely cheap and expendable, so mathematical models are less important.

Engineering is a science AND art.  It involves designing AND making, with cost AND elegance balanced.

Don’t build software as if it were bridges.  The source code *is* the design!  Customers are not paying for the source code.  It is the design of the software system.  Programs themselves are the models.  The customer in the end desires working software.

How to Build Awesome Teams

The guys from Hashrocket talked about their hiring process and what they felt made for great teams, core ideas:

  • Hiring
  • Transparency
  • Community
  • Methodology
  • Environment

They frown on:

  • Micro-management
  • Penny pinching (they still budget)
  • Death marches
  • Meetings (daily stand-up, monthly mission control)
  • Hierarchy
  • Future-proofing

When they hire candidates they look for people who are smart and get things done.  They look for individuals who like to communicate, participate and contribute back to the community.  They need to be a cultural fit.  They look at their online presence, see what they’re Twittering about, review their work on GitHub (code, commit hygiene, etc).  They also require a candidate to subject themselves to a week long interview, where they pair program and go to after-hours with the team.

Keynote: Tom Preston-Werner (GitHub)

Today everyone can start a business on their computer.  What you need though is an idea.

  • go to user groups (learn about people’s problems)
  • drink (seriously, it makes you honest)
  • find people you can work with (co-founders)
  • build something you love (that inspires you)
  • look for untapped potential (who are the early adopters, and how awesome can it become?)
  • keep your day job (not having time is a poor excuse)
  • fight entropy (keep it simple, and get stuff done)
  • ship it (or you might as well not exist) [ship it squirrel]
  • charge money (investors only care about money, not your company, so make customers your investors)
  • have fun! (“drink-ups” where drinking is sponsored)
  • never give it up (it’s going to be hard, don’t let fear hold you back)

If you want to have a $100,000 a year salary, you just need to get about 500 subscriptions at $12/mo (with a few premium or mid-level price options).  It’s totally realistic and in your grasp.  Don’t be afraid of not knowing something … people just like you started out with just an idea.

Rollback with Yum History

July 8th, 2010

Ever want to install software and find out your missing libraries? So you go and install all the libraries and dependencies.  Then afterwards you decide you don’t want the software after all. You’ve installed megabytes worth of packages and dependencies.  You want to get back to your previous state, but are not quite sure what needs to be removed. Now what do you do? You can’t remember what was installed or all the dependencies that came with those packages, but you roughly remember the day and time you played around with the new software.

This is where yum history comes to the rescue.  (If you’re using a RPM friendly flavor of Linux, like Fedora or RedHat). You can get a history of what was installed by using

$> sudo yum history

Loaded plugins: presto, refresh-packagekit
ID | Login user      | Date and time    | Action(s) | Altered
---------------------------------------------------------------
47 | System <unset>  | 2010-07-08 11:54 | Update    |   11   
46 | <nicholas>      | 2010-07-07 22:53 | Update    |    2   
45 | <nicholas>      | 2010-07-07 22:50 | Install   |   49
44 | <nicholas>      | 2010-07-03 18:52 | Install   |    8
...more

You can then find out what was installed that day and remove it:

$> sudo yum history undo 45

Rails on Windows with JRuby

December 12th, 2009

Getting a working Rails stack on Windows can be a frustrating experience.  Rails was born in the Linux/Unix world so there are many libraries assumed to be on your system, but are not part of a regular Windows OS installation.  Below I share my own personal experience with getting up and running with Ruby and Rails using JRuby, which for me was the best way to go.

Requirements

Note the links above might move or change so you can visit (java.sun.comwww.jruby.org, www.postgres.com) and poke around to find them.

Installing Java and JRuby

This part is very easy.  Just double click the Java installer and follow the directions.  It will install Java to a location in your C:\Program Files (or the location you specify).  Do the same with JRuby.  I like to have JRuby live right under C:\jruby-1.4.0 so it is easy for me to get to (by default it will also go under C:\Program Files).

Now we need to edit our Windows environment and make sure that JRuby and Java can be seen on the path.  You’ll want to go to your Start > Control Panel > System > Advanced system settings and click the Environment Variables button.

Windows Environment VariablesYou might already see a JAVA_HOME value under User variables (and that’s ok).  You might even see one for JRUBY too.  I like to have the environment variables for Java and JRuby available at the system level however.

Click New under the System variables section and add entries for JDK_HOME and JRUBY_HOME as you see in the example on the right.  Click OK when you’re done.

Now open a command prompt window (Start > Accessories > Command prompt) and type in echo %JDK_HOME% and hit enter.  You should see the value you set, do this for (echo %JRUBY_HOME%) as well. Both should return the values you set previously in the Environment Variables window.  The last thing to do is add these values to your PATH.

Return to the Environment Variables window and edit the PATH value under system variables by selecting it and hitting Edit.  Entries are separated by a semi-colon (;) so make sure you add one to the front of the entries you’re adding unless there is one already there:

;%JDK_HOME%;%JRUBY_HOME%

Now after that’s done close and reopen your command prompt window (so that the new values are loaded).  Type echo %PATH% and you should see the two paths at the end of the results:

>> echo %PATH%
C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;
C:\Windows\System32\WindowsPowerShell\v1.0\;
C:\Program Files (x86)\QuickTime\QTSystem\;C:\Program Files(x86)\Git\cmd;
C:\Program Files\Java\jdk1.6.0_17;C:\jruby-1.4.0\bin

Now that all our paths seem in order we can further verify everything is working right:

>> java -version
java version "1.6.0_17"
Java(TM) SE Runtime Environment (build 1.6.0_17-b04)
Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01, mixed mode)
>> jruby -v
jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02) (Java HotSpot(TM) 64-Bit)

Welcome to JRuby

At this stage you should have a working copy of JRuby.  JRuby is just like Ruby, except it uses Java and not the MRI/YARV interpreter.  Most of the commands you might be used to on a regular Ruby stack work the same in JRuby, except often you use jruby -S to run your commands rather than ruby.  Let’s start installing a few of my favorite gems:

jruby -S gem install rails haml nokogiri shoulda factory_girl cucumber webrat ^
rspec rspec-rails will_paginate ZenTest redgreen fastercsv RedCloth paperclip ^ 
nifty-generators jruby-openssl

That should be plenty to get you going on most any Rails/Ruby project.  Watch the output as the gems are installed, rarely should you run into trouble.  If a gem is complaining about installing you can add the –platform=java flag to the end to try and force it to install under the Java platform (for example):

>> jruby -S gem install grumpy_gem --platform=java

Connecting to Postgres

You probably can use any Rails supported database, but I prefer Postgres.  It’s a free database that is available on a variety of platforms (much like MySQL) but a little more powerful.  I also find it easier to work with on JRuby so it’s the one I’ll use here.

Run the PostgreSQL installer.  Follow the on-screen directions.  During the installation, however it is important to make sure you install the optional JDBC drivers, don’t forget this step (if you do you will have to run the Postgres Stackbuilder application later and install them).

The installer is going to add a ‘postgres’ user onto your system and require you to restart at some point.  Once the installation is completely finished you can open your command prompt and install the Rubygem for connecting to Postgres:

>> jruby -S gem install activerecord-jdbcpostgresql-adapter

I am not going to go into details about how to set-up users, etc. in Postgres.  You hopefully are familiar enough with database software to know how to do there is plenty of information on the Web to figure this out, just Google it.  Postgres comes with some nice GUI tools that make it easy to do and it is pretty straight forward.

Now there is one caveat with the Postgres ActiveRecord adapters and JRuby.  There’s a known issue with creating and dropping databases in Rails.  So in order to get things like rake db:create:all to work you need to type inside your Rails application root:

>> jruby -S script/generate jdbc

That is pretty much all there is to getting a full stack up and running on Windows.  I have started developing in the past week using JRuby on Windows along with Rubymine as my editor and GIT as my source control.  I’ve been pretty happy with it thus far.  I still enjoy developing most on my MacBook Pro – but it was a fun experience exploring JRuby (I’ve been playing with JRuby and Ant working together).

4-Bit Server VM 1.6.0_17) [amd64-java]C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\Wind
owsPowerShell\v1.0\;C:\Program Files (x86)\QuickTime\QTSystem\;C:\Program Files
(x86)\Git\cmd;C:\Program Files\Java\jdk1.6.0_17;C:\jruby-1.4.0\C:\Users\Nicholas>echo %PATH%
C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\Wind
owsPowerShell\v1.0\;C:\Program Files (x86)\QuickTime\QTSystem\;C:\Program Files
(x86)\Git\cmd;C:\Program Files\Java\jdk1.6.0_17;C:\jruby-1.4.0\bin

Nokogiri – Cut with Precision

November 15th, 2009

Many times we as developers have to deal with complex data, be it an ActiveResource result set or a HTML/XML document.  Trying to parse data out of these using for each and nesting loops within loops can be cumbersome.  A more elegant solution is to use nokogiri and xpath.

Nokogiri is a type of Japanese saw, it also is a gem in Ruby that you can use to easily deal with XML or HTML documents.  (hint, ActiveRecord and ActiveResource objects both have to_xml methods).  You can easily install nokogiri (make sure you have libxml2 development packages installed, as the gem requires these to be properly built).

$ sudo gem install nokogiri

Now consider the following XML document: foods.xml

Before we can work with our data we need to read XML into Nokogiri. This is easy to accomplish:

> require 'rubygems'
> require 'nokogiri'
> doc = Nokogiri::XML.parse(File.read('foods.xml'))
=> #<Nokogiri::XML::Document:0x3f930c9db884 ...

What we are returned is a Nokogiri document which is a collection of Nokogiri elements and text objects. The document supports seaching (selecting a subset of nodes, or nokogiri nodeset) by both CSS selectors or XPath notation. These are returned as an array of elements and text objects.

So for example if we wanted to know all the names of the food items in our document we simply say:

> doc.xpath("//name").collect(&:text)
=> ["carrot", "tomato", "corn", "grapes", "orange", "pear", "apple"]

If we were interested in the entire node we could leave off the .collect(&:text). What if we wanted to select all the names of food items that were best baked?  This requires us to use what’s called an axis – we will first need to find the element “baked” but then go back up our XML elements to find which food the item is inside.

> doc.xpath("//tag[text()='baked']/ancestor::node()/name").collect(&:text)
=> ["pear", "apple"]

What if we were only interested in vegetables that were good for roasting?  Just add //veggies:

> doc.xpath("//veggies//tag[text()='roasted']/ancestor::node()/name").collect(&:text)
=> ["carrot", "tomato"]

What about if we wanted to know all the tags ‘corn’ had?  Again this is very easy:

> doc.xpath("//name[text()='corn']/../tags/tag").collect(&:text)
=> ["raw", "boiled", "grilled"]

We can even do searches matching the first character.  Let’s say we wanted to know all the food items that started with the letter ‘c’:

> doc.xpath("//name[starts-with(text(),'c')]").collect(&:text)
=> ["carrot", "corn"]

You have to admit this is pretty cool stuff.  You could also use [contains(text(),'rot'] and get back just carrot, useful when you want to do a partial match.  Axis combined with selectors give you a wide variety of options for parsing your dataset.  You can also match using operations.  See the links below for resources on the variety of options available.

Xpath is Powerful

Xpath lets us select XML elements, attributes and text without having to write cumbersome recursive, nested loops. Below are  links to online resources and tutorials.  The next time you have to dig through an XML document or ActiveResource result, don’t use recursive, nested loops; instead, consider a Japanese saw – nokogiri.

Learn More

Writing Stories with True Value

November 5th, 2009

A lot of folks who dive into the world of Agile (be it Scrum or XP) will need to learn how to write stories.  Stories are an expression of a feature which provides value.  They typically take the format of “As a {user-role} I need/want to {some functionality} so that {value}.”  I coach people to use what I call the 3Ws:  Who is the user, what is it they want to do, and why do they want to do that?  Any well written story should be able to answer those three questions.

The following is an example story that isn’t written well:

“As a manager I want documentation produced so that the software can be understood by people.”

Who’s the user here, the manager – really?  It doesn’t sound like he’s the one using the documentation, “people” are (whomever that is).  ”Documentation produced” also is a task – not an expression of functionality.  The value statement is also dubious “so that software can be understood,” but why is that valuable for “the people?”  Reading this story leaves you with too many unanswered questions and is overly vague.  You can quickly see this story fails to answer our 3Ws.

Instead, a better approach, is to make documentation part of the done criteria (or acceptance) of the actual feature the story should be addressing.  Remember with stories we want to end with a demonstrable unit of work.  If the feature was to add the ability to send e-mail after someone comments on a blog post, for example, you might have something like this:

“As a user who’s commented on a blog post I want to receive a notification anytime additional comments are added so that I keep up with the conversation.”

This story is much better.  We know that we’re talking about people who have posted a comment on a blog post.  We know that they want to receive a notification anytime new comments are added.  They want this because it helps them keep up with the conversation.  Anyone could read this story and understand what needs to happen. What’s even better is it doesn’t imply implementation.  It doesn’t say I should receive an SMS message, and e-mail or a letter in the mail.  Those details will be discussed when this story is planned.

In order to complete this work the whole vertical slice has to be addressed:  code business logic, design e-mail template/layout, test feature, write documentation.  There isn’t a need to have a separate story for this — I’d advise against it, because your feature really isn’t done if documentation is a requirement.  When you finish a feature you want it to be done, done.

Bad Smells

  • Story not addressing a vertical slice typically have “developer” or “manager” as the user-role.
  • Story doesn’t express business value or functionality, but instead expresses a task.
  • Story has dependencies on other stories being done first.
  • Story talks about implementation.

webOS on Mac OS X

July 25th, 2009

webOS is here!  If you’re unfamiliar with the term, it’s the name of the development platform that powers the Palm Pre.  Unlike the iPhone (which requires esoteric knowledge of Objective-C and Cocoa), webOS uses web technologies like JavaScript/Prototype, CSS and HTML5.  This makes it really easy for web developers to pick up and develop on the phone.

The only snag I’ve run into on my MacBook Pro is that the novacom service isn’t started automatically when you launch the emulator.

This error message greets you when you first run the Palm emulator.

This error message greets you when you first run the Palm emulator.

The problem is the service/daemon that the emulator depends on has some wonky permissions, well technically they’re called ‘dubious’.  So they get refused when the installer script does its clean up.  You can start the daemon manually with /opt/bin/novacomd — but that gets annoying after a while.  So to fix it you simply need to enter the following two commands in your terminal:

sudo chmod 644 /Library/LaunchDaemons/com.palm.novacomd
sudo /opt/nova/bin/post-install.sh
$ sudo chmod 644 /Library/LaunchDaemons/com.palm.novacomd
$ sudo /opt/nova/bin/post-install.sh

The first line repairs the permissions so MacOS X doesn’t complain about any dubious looking files when you attempt to add them to launchctrl.  (The root of the issue is the com.palm.novacomd cannot have write permissions for the group, which it does post-install!)  The second command just re-executes the post-install script, and properly installs the service .

Now when you reboot your Mac you don’t need to remember to start-up any daemons before you begin development.  Enjoy!

Running TextMate, Terminal and the Mojo SDK (running VirtualBox) with the Palm Inspector

Running TextMate, Terminal and the Mojo SDK (running VirtualBox) with the Palm Inspector

You can learn more about how to get started developing below:

http://developer.palm.com

http://www.youtube.com/watch?v=YXS3SQauwPE (O’Reilly video)

Common Agile Observations

May 7th, 2009

Heading up the AgileAustin workshops I get to meet with a lot of passionate Agile practitioners working in the real world. What I find amazing is how we all have similar experiences and common challenges.  Below are some of the common themes I’ve observed from my own career and also from talking with other ScumMasters, maybe you’ve seen the same thing:

Adoption

This is the holy grail question of anyone looking to start going Agile, “How do I get adoption at my company?”  Adoption is best started at the ground level (according to Ken Schwaber).  The reason he identifies this is because of command-and-control (what I call ‘old-school management thinking’).  Teams who are under command-and-control only act when they’re told to, and don’t take the initiative on their own.  Also managers who practice command-and-control believe it is their job to provide the team with answers (both what and how), instead of waiting for the team to figure out a solution on thier own (and that includes making mistakes).

Understanding the New Roles

So if it isn’t the role of the manager to provide answers, what is their role?  The role of a manager in an Agile organization is that of a servant leader.  Their job is less about control and more about service — you help remove impediments to the team.  A good servant leader values collaboration, trust, empathy and the ethical use of power.  Your primary objective is the enhance the grow of individuals, increase teamwork in the organization and personal involvement.

Product owners are responsible for prioritizing and organizing the backlog (a list of features to be completed).  They develop acceptance criteria so that the team understands what is required for a feature to be considered “done” and they work with the team to estimate the features (usually by size).  They own the “what” question (what should be built, what’s most important or not, etc) and define releases.

In an Agile organization the Product Owner is the “single wringable neck.”  This is to say they’re ultimately responsible for the product’s success or failure.  The are probably the most important individual on a Scrum team since they decide what features get built and thereby set the direction of the team.

Team members, other known as ‘pigs’ (because they are committed to doing the work), are responsible for selecting the work they take on.  They do so inside a time-box, meet frequently, and at the end demonstrate their work.  The team is responsible for the “how” question (they own the implementation details, design, etc).

Anyone else is a ‘chicken’, that is someone who contributes but isn’t committed.  (Mike Vizdos has a great cartoon explaining the pig and chicken metaphor).  Chickens can be anyone from an outside consultant or subject matter expert to a CEO.  They might attend the daily meetings with the scum team, but they are not participants in these meetings (because they’re not committed to the work).

Assuming it’s About Developers

These role changes are often not fully embraced or recognized.  Organizations might take some steps in the right direction:  they create a backlog, they move to 1-2 week sprints, they might even have daily stand-ups and demos/retrospectives.  Scrum is more than just an adjustment to the lifestyle of the developer.  Their needs to be a strong product vision that everyone understands and is behind.  Concensus is very important, people who are not sold on an idea are not going to put their passion into the work.

This is where leadership needs to inspire (not measure) everyone on the team to the goal set before it.   The team has to be self-directing and encouraged to self-organize to reach it.  Scum is about changing how your organization approaches problems and comes together to work as a whole team – manager, product owner, and the scrum team.  I recommend Jean Tabkia’s Collaboration Explained for some exellent inight into teams and how they work.

If there is a manager-type stomping around telling people how, what and when to get things done – you’ve missed the boat.

Individual Performance Evaluation

The last big impediement I often observe is what I call ‘team self-destructive behaviors.’  These are agile anti-patterns that primarily come from old-school management philosophies and lack of education about teamwork.  Some of these ideas are:  workers are inherinantly lazy, workers need to be told how to properly do their job, worker productivity equals hours worked, etc.  What ends up happening in some organizations is that managers task out the work for the team, will estimate it for them, and even sometimes assign tasks to specific members.  (In short, the team is deprived the ability to be self-directing and self-organizing).

Any behaviors that focus on the individual and measures individual performance, rather than the team’s performance, are counter productive to building a good team!  When you value individual performance a strange rivalry is born between team members which stops them from working as a team.  For example:  Steve and Bob are both working on different features.  The feature Bob is working on is highest in priority.  Steve finished his feature early – instead of helping Bob with his, he moves on to another story to show Bob up and make himself (Steve) look good to the management.  The sprint ends without Bob finishing the most important feature.

Ester Derby wrote an interesting article about performance and appriasial.  She suggests that the idea of ‘merit pay’ should maybe fall out of style.  We don’t want to reward people for knowing how to work within the system, we want to reward people for improving the system.

BDD Step-by-Step Example (part 1)

March 29th, 2009

Before we can begin doing BDD (behavior driven development) we need to install some tools to help.

sudo gem install cucumber rspec-rails webrat

Next I go to my Rails application and run some scripts to get the basic set-up inside my rails directory structure:

script/generate rspec
script/generate cucumber

Now in the newly created features folder I will create a file called user_administration.feature. This file will contain all my scenarios for this feature. After spending some time thinking about how to express my features I come up with two basic ones to start:

Feature:  User Administration

So that I can control access to the application
As an admin
I want to manage users

Scenario: Create a new user account
  Given no user "joetest"
  When I create a new user "joetest" with password "skymonkey"
  Then the user "joetest" can log in with password "skymonkey"

Scenario: Suspend an existing user account
  Given a user that is active
  When I suspend the user
  Then the user can not log in

Now I run the following command to have cucumber generate snippet methods that I can then copy into a step definition.

script/cucumber features/user_administration.feature

I create a new file named user_steps.rb in the features/step_definitions folder. Now that we have our step definition snippets the next job is to take them from “pending” into something meaningful. We’ll start with the first scenario. After giving it some thought it might end up looking like this:

Given /^no user (.+)$/ do |login|
  lambda{ User.find(login) }.should raise_error(ActiveRecord::RecordNotFound)
end

This first part sets us up. It allows us to express the given state, in this case not finding a user named “joetest”.

When /^I create a new user "(.+)" with password "(.+)"$/ do |login, password|
  visit new_user_path
  fill_in "user_login", :with => login
  fill_in "user_email", :with => "#{login}@test.org"
  fill_in "user_password", :with => password
  fill_in "user_password_confirmation", :with => password
  click_button "Sign Up"
end

Next we describe the action we’ll be taking. This code simulate the submission of a form, using Webrat. We take this approach because it makes us exercise the whole stack views, controller and models. It’s just short of me going to the site with a browser.

Then /^the user "(.+)" can log in with password "(.+)"$/ do |login, password|
  visit login_path
  fill_in "login", :with => login
  fill_in "password", :with => password
  click_button "Log In"
  response.should contain("Logged in Successfully")
end

Lastly we test our outcome, that the user the admin created is able to log in. So the end result is test that describes the behavior of an admin creating a new account for a user, and they are able to successfully log in.

Along the way you may find tests go red (fail), this is normal. You want to write code (just enough) to make them go green, then continue to write more test code. Keep repeating this until all your tests run green.

Also the nice thing about step definitions is that they can be reused in other tests. We’ll likely have other scenarios where we create a new user or try to log in as one. Our code stays dry and we (the developer) stay confident. (Now it’s sounding like a anti-perspirant commercial, so I’ll stop now.)

Going Outside In with Rails, Cucumber, and RSpec

March 28th, 2009

Traditionally when you do emergent design, at least in my experience, you go from the inside out. Work from your model into your controller and views. Since web applications are so heavily focused on user interaction it sometimes is better to work from the outside in. When working with a Rails application think instead of your views and work into your controller and models.

Understand the UI first before you write any code. Use things like paper prototyping to understand real user data needs and challenges. Paper prototyping is a cheap and easy way to understand what objects and data a user will be interacting with for a given task. Keep your session short, and focus only on the feature you’re developing. You can find examples of paper prototyping on YouTube.

Get boot-strapped with Cucumber, RSpec and RSpec-Rails, and dive straight into BDD on your Rails project. To do this you need to install various gems to support your development efforts:

sudo gem install cucumber rspec-rails webrat
cd my_rails_project
script/generate cucumber
script/generate rspec

That’s all you have to do to get going! You’ll notice that you’ll have a new features folder and the usual spec folder (from RSpec). You will want to describe your features as stories and scenarios for Cucumber in the features folder. Start with your views when you think about scenarios.

The basic gist of it is just like regular TDD, except we are focusing on behavior:

  1. Choose a single Cucumber scenario
  2. Create a failing step definition in Cucumber
  3. Create a failing spec in RSpec
  4. Write code to make the spec pass (red/green refactor)
  5. Repeat 3 and 4 until the scenario is passing

In the process of making the scenario pass you’ll find yourself discovering models and controllers that will need to be written or updated. Taking this type of approach helps you focus on writing just enough code to get your tests passing, without over engineering, and has the added benefit of you writing testable code. (Remember TDD/BDD is not so much about testing as it is about communication, documentation and design!)

Your finished scenario should touch a complete vertical slice of your application, that is to say, model->controller->view. It should provide some real value to the user. Agile development philosophies frown on partial feature delivery. A schema, security filter or screen alone is not a feature. If you write scenarios from the user’s perspective you’ll avoid this pitfall.

To learn more about Cucumber and RSpec (either in Rails, or plain Ruby) I would recommend checking out Pragmatic Bookshelf, they have a beta book on RSpec.