Category Archive for BDD

Cucumber step definition tip: Stubbing time

February 02, 2009

When writing Cucumber scenarios, you'll occasionally need to describe a behavior that is dependent on time passing. For example, at weplay we want to ensure that a password reset link expires after one week.

In a RSpec code example, you might be used to stubbing time in this situation:

1
2
3

now = Time.now
Time.stub!(:now).and_return(now + 8.days)

This doesn't work out-of-the-box in a Cucumber step definition. By default, Cucumber doesn't include any of RSpec's mocking or stubbing support. This makes sense because Cucumber is a tool for acceptance testing (full stack), where stubbing and especially mocking should be discouraged and only used for very specific purposes (like stubbing time!).

If you find yourself in this situation, you can enable RSpec's stubbing support in your Cucumber step definitions by adding the following code to env.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

require "spec/mocks"

Before do
  $rspec_mocks ||= Spec::Mocks::Space.new
end

After do
  begin
    $rspec_mocks.verify_all
  ensure
    $rspec_mocks.reset_all
  end
end

Now you can go ahead and create a time_steps.rb like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

When /^(\d+) minutes pass$/ do |minutes|
  now = Time.now
  Time.stub!(:now).and_return(now + minutes.to_i.minutes)
end

When /^(\d+) hours pass$/ do |hours|
  now = Time.now
  Time.stub!(:now).and_return(now + hours.to_i.hours)
end

When /^(\d+) days pass$/ do |days|
  now = Time.now
  Time.stub!(:now).and_return(now + days.to_i.days)
end

RSpec will clear your time stub at the end of each scenario.

The RSpec Book is now in Beta

January 29, 2009

The RSpec Book

I’m very happy to report that The RSpec Book, which I’ve been collaborating on, is now available for sale as a Beta Book from the Pragmatic Bookshelf. David Chelimsky posted an announcement to the rspec-users mailing list which sums it up quite well. To quote:

On behalf of all the authors, I’d like to extend a special thank you to all of you who have contributed to the software and the conversation around RSpec, Cucumber, and BDD in general. RSpec would be nothing without the community that has evolved around it, so thank you, thank you, thank you.

Enjoy! And if you do pick it up, please let us know what you think. We’re eager to improve it based on the feedback we get.

Story Driven Development slides posted

April 26, 2008

Just wrapped up my Story Driven Development talk at GoRuCo 2008. There were some great questions at the end, and I'm looking forward to more hallway track conversations about SDD.

Download the slides as a PDF (1.6 MB)

Note: Confreaks is recording the talks today, so a full video of my presentation should be online soon.

Presenting at GoRuCo in NYC

February 09, 2008

I just received word that my proposal to speak at NYC’s very own second annual GoRuCo has been accepted. The conference is set for Saturday, April 26th at Pace University downtown. My topic will be the same as my Scotland on Rails presentation.

I’m really excited to have the opportunity to present at home, in front of many of my friends and colleagues. Now I just need to make sure I don’t suck. Better get back to working on my slide deck…

Presenting at Scotland on Rails

January 31, 2008

I’m going to be presenting on Story Driven Development at the first Scottish Ruby on Rails conference, Scotland on Rails. I’ll be exploring the concept of Story-first development from both an agile process and implementation standpoint.

Topics will include:

  • Collaborating with the product owner on stories
  • What makes good stories and scenarios?
  • Driving stories with a browser simulator like Webrat
  • Potential pitfalls and ways to avoid them

If you’re going to be in Edinburgh for the conference, let me know.

Webrat 0.1.0 released

December 08, 2007

Last week I pushed Webrat 0.1.0 out the door. So far the response has been great, and I’ve already received a couple patched (Thanks, David!). Here’s a quick usage example (from the README):

1
2
3
4
5
6
7
8
def test_sign_up
    visits "/"
    clicks_link "Sign up"
    fills_in "Email", :with => "good@example.com"
    select "Free account"
    clicks_button "Register"
    ...
  end

Behind the scenes, this will perform the following work:

  1. Verify that loading the home page is successful
  2. Verify that a “Sign up” link exists on the home page
  3. Verify that loading the URL pointed to by the “Sign up” link leads to a successful page
  4. Verify that there is an “Email” input field on the Sign Up page
  5. Verify that there is an select field on the Sign Up page with an option for “Free account”
  6. Verify that there is a “Register” submit button on the page
  7. Verify that submitting the Sign Up form with the values “good@example.com” and “Free account” leads to a successful page

Take special note of the things not specified in that test, that might cause tests to break unnecessarily as your application evolves:

  • The input field IDs or names (e.g. “user_email” or “user[email]”), which could change if you rename a model
  • The ID of the form element (Webrat can do a good job of guessing, even if there are multiple forms on the page.)
  • The URLs of links followed
  • The URL the form submission should be sent to, which could change if you adjust your routes or controllers
  • The HTTP method for the login request

A test written with Webrat can handle these changes smoothly.

SVN is at http://svn.eastmedia.net/public/plugins/webrat/. Check out the full README.

How much money is your slow build wasting?

September 09, 2007

Recently, I've noticed a trend of unnecessarily slow builds hurting development velocity. I fear that developers may be overlooking the fact that a build composed of great code with great coverage is only truly useful if it can be run quickly.

Slow builds hurt development velocity in two primary ways. First, the developers must wait while the build is running. Remember how compiling provided ample time for extra coffee breaks or checking email and RSS feeds? If running the build is filing that gap, you've got a problem.

The second impact is harder to quantify, but equally important. Slower builds get ran less often. That means an increased feedback loop between code modifications and confirmation of their correctness. Tightening that loop is a central tenet of TDD, and it is what allows programmers to efficiently write code with confidence that it works.

If your continuous integration build fails because someone didn't run all of the tests before checking in, you're seeing another side effect of this problem. Continuous Integration is there to catch integration issues, not serve as a substitute for running the build locally.

Below I've setup a simple calculator to estimate the money directly wasted by a slow build. Five day work weeks are assumed. On a recent project I roughly estimated a loss of at least $7,500 per week due to unnecessarily slow builds.

Full-time developers
Old build duration minutes
New build duration minutes
Development rate dollars per hour
Builds per developer per day

The slow ass build is wasting at least $0 per week. Ouch.

Monitor builds from your desktop with CCMenu

September 01, 2007

So let’s just say you’re being a good developer and you’re writing tests. They are have good coverage (because you’re writing them first), they run fast, and you’ve setup a continuous integration (CI) server (like CruiseControl.rb) to run the build on every checkin.

CI servers can generally send email notifications for build failures and fixes, but when I’m in heads down development I often don’t check my email for hours at a time. Hours is far too long for a build to sit broken, especially when there are many other developers working on the project.

CCMenu tightens that feedback loop so I get immediate build status information in my menu bar. It uses Growl for notifications and provides a dashboard view of each project and its detailed build status. That’s a big improvement, and will help hold me over for a bit longer while I convince my boss to get us a build-controlled traffic light in the office.

GoRuCo talk: Contexts, mocks, and stubs. Oh my!

April 21, 2007

Presented by Trotter Cashion

Beyond Test::Unit

  • FlexMock
  • Mocha and Stubba
  • Test/Spec – Brings RSpec to Test::Unit
  • spec-unit—Written by Trotter. Similar to Test/Spec. Has nested contexts.
  • RSpec—Total Test::Unit replacement.
    • Switching to RSpec is not necessary to use these concepts.

Why should you care?

  • Contests
    • Let you organize specs with shared setup
    • Isolate specs that should not be affecting eachother
  • Mocks versus stubs
    • Mocks validate the method calls
    • Stubs don’t care
  • Use mocks when you need to be sure
  • Use stubs when you don’t care—helps avoid brittle tests

What I’m not talking about

  • RSpec
  • Mocha and stubba
  • Test/Spec

That leaves…

  • Spec-unit
  • FlexMock

Risks

  • Active development—APIs still changing
  • You might get too excited

Benefits of mocking

  • More focused testing
  • Better application design
    • Mocking lets you think about the interface without building client classes

Code walkthroughs followed…

DRY RSpec by generating specifications in a loop

April 02, 2007

Occasionally, when doing Behavior Driven Development with RSpec, I'll need to specify a number of cases where the internal structure of the spec is identical. Specifying a simple function, for example, might lead to code like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
context "My one adder" do
  setup do
    @processor = Adder.new(1)
  end
  
  specify "should add 1 and get 2" do
    @processor.add(1).should == 2
  end
  
  specify "should add -1 and get 0" do
    @processor.add(-1).should == 0
  end
  
  specify "should add 0 and get 1" do
    @processor.add(0).should == 1
  end
end

Given the information being conveyed, this is very verbose and also not DRY. (When writing specs, it's important to note that DRY is not the #1 priority—that's clarity—but overly "wet" code can still increase the chance of bugs.)

One way to reduce the length of the spec is to condense the expecations into a single specification block:

1
2
3
4
5
6
7
8
9
10
11
context "My one adder" do
  setup do
    @processor = Adder.new(1)
  end
  
  specify "should add numbers correctly" do
    @processor.add(1).should  == 2
    @processor.add(-1).should == 0
    @processor.add(0).should  == 1
  end
end

This introduces two disadvantages. First, a failure on any expectation in the spec will cease execution of the specify block, and thus all further failures will be masked. Second, the specdoc produced by the sample is not as clear in conveying the expectations for the object.

Thanks to a quick tip from David Chelimsky, it's easy to have my cake and eat it too:

1
2
3
4
5
6
7
8
9
10
11
12
context "My one adder" do
  setup do
    @processor = Adder.new(1)
  end
  
  expected_results = {-1 => 2, -1 => 0, 0 => 1}
  expected_results.each do |input, result|
    specify "should add #{input} and get #{result}" do
      @processor.add(input).should == result
    end
  end
end

This produces the ideal specdoc, is DRY, and will not allow one failure to mask another during a spec run.