Making the Most of BDD, Part 1

Hi, I’m Mary-Anne. I’m a senior Ruby developer here at Envato. One of the things that I love about my job is that it gives me the opportunity to use one of the practices I am most passionate about - Behaviour Driven Development, or BDD.

BDD is the practice of writing functional tests before you write unit tests. It incorporates the older practice of test driven development (TDD), the art of writing unit tests before you write code. At Envato, our code is written in Ruby, our unit tests in RSpec and our functional tests are in Cucumber, but many of these principles can be applied to other languages and frameworks.

Behaviour Driven Testing consists of cycles inside cycles. For BDD, The inner TDD loop (Write a failing unit test -> Make it pass -> Refactor) is wrapped inside a slower BDD loop (Write a failing feature test -> TDD until it passes)

It’s not (just) Mortein*

Louie the fly - Australian Icon We all know that automated testing is a great way to kill bugs. But BDD is about so much more than that…

  • It can be the driver of your programming cadence
  • It focuses your attention on your users’ experience
  • It is the living documentation of your system
  • It informs the structure of your application

Let’s explore each of these in turn. Along the way, I’ll give you some tips for getting the most out of Cucumber and RSpec.

*A popular Australian brand of bug-spray

Feel the Rhythm

My grandfather's clock BDD drives your programming cadence like the pendulum drives a grandfather clock. You start with a clean build; imagine the clock striking 1. The first thing you do is write a failing feature test - a Cucumber scenario in our case. Your first job is to get one step to pass. To do that, you’ll need to write some code - but first you write a failing unit test. You make that pass, then refactor the code. In getting a single Cucumber step to pass you’ll go through many red - green - refactor cycles in the unit tests, just like the second hand will tick many times before the minute hand moves on. Eventually the whole feature will pass and you can run the entire build; you’ve progressed to 2 o’clock!

You can use the inner cycle as a fun way to make sure each partner gets a turn when you’re pairing. One person writes a test, the other writes the code to make it pass, the first person refactors, then it’s the second person’s turn to write a test. When you’re in a good rhythm here it’s like playing ping pong, each person bouncing ideas off the other.

The secret to productivity in BDD is getting your second hand to run quickly so you can write your tests in tiny increments. This means you’re never far away from working code. The feature test gives you guidance throughout this process, helping to make sure you’re always progressing towards your end goal.

If you’re working in a large Rails application, RSpec startup time is too slow to develop this rapid fire cadence. You really need to be running something like Zeus or Spring. These are gems that pre-load your rails environment for a rapid start to your tests.

Keep that rapid-fire rhythm going by only running the single spec you are currently working on, until it goes green. Then run the whole file, before going back to the cuke.

It’s also important to keep your hour hand - your whole build - running smoothly. Functional tests run orders of magnitude more slowly than unit tests. Write functional tests only for the major happy path scenarios. Work through the edge cases in the unit tests. Write some integration tests to make sure nothing has fallen through the gaps between your unit tests. If your whole build takes more than 10 minutes to run, set it up to run multiple tests in parallel.

Tips:

  • Use a pre-loader like Zeus or Spring
  • Start zeus in one terminal pane and run your tests in another
  • Run a single test by line number, e.g. zeus test spec/models/my_model_spec.rb:50
  • Write the most unit tests, write the least functional tests
  • Try test ping-pong when you are pairing

Cuke it Out

Duke the Cuke Here at Envato, some of our teams work together to develop Cucumber scenarios for our sprint work at the beginning of the sprint. When you write scenarios together you’ll find the whole team develops a good understanding of the requirements for your sprint, and you will discover many more of the ‘gotchas’ than you would if you wrote scenarios individually.

Writing the scenarios from the user’s perspective gives you much more empathy for them than you would otherwise have, and helps identify areas where the user experience needs more attention.

You can write out the whole set of scenarios at your sprint start. They then become a good guide to your progress throughout the sprint.

Here at Envato we’ve added a few tools to our Cucumber toolbox to help with debugging. For example, we have defined a step ‘Then I debug’ as follows:

1
2
3
4
5
6
7
8
9
10
11
Then /^I debug$/ do
  begin
    require "pry"
    pry binding
  rescue LoadError
    require 'irb'
    require 'irb/completion'
    ARGV.clear
    IRB.start
  end
end

This definition reverts to IRB for those who are not using Pry yet. If you’re still using IRB, this is worth a read: Rubyists, It’s Time to PRY Yourself Off IRB!

Then there’s ‘Then show me the page’:

1
2
3
Then /^show me the page$/ do
  save_and_open_page
end

Tags are a great way to organise your tests. You can tag your tests with anything you want then filter the set of tests that are run according to your tags. You can also define actions to take before or after your tag, to help make your tests more readable. Here’s an example that ensures a feature flip is turned on for a test:

1
2
3
Before '@special_feature_flip_on' do
  Flip::FeatureSet.instance.strategy('global').switch!(:special_feature, true)
end

Tips:

  • @wip allows you to check in your work-in-progress without breaking the build
  • Run all @wip tests with rake cucumber:wip
  • @selenium @inspect allows you to view the application at each step

Next Time…

In Part 2 of Making the Most of BDD we’ll look at BDD as documentation, how it impacts your architecture, and why it’s important to be pragmatic.

Want more?

Check out the Prezi