Marking Work in Progress

In BDD, you’re typically working on getting just one spec at a time to pass. Trying to tackle too many features at once leads to the kinds of complicated, untestable designs that BDD seeks to avoid.

On the other hand, it can be extremely productive to sketch out several examples in a batch. You’re thinking of all the things a software component needs to do, and you want to capture the ideas so you don’t forget anything. RSpec supports this workflow really well, through pending examples.

Starting With the Description

While you were writing the coffee spec earlier, you may have been thinking ahead to other properties of coffee with milk: it’s lighter in color, cooler, and so on. While these behaviors are on your mind, go ahead and add them inside the with milk context as empty examples:

 it​ ​'is light in color'
 it​ ​'is cooler than 200 degrees Fahrenheit'

There’s no need to fill these in; just let them stand as they are while you’re working on other specs. Here’s what RSpec shows on the console when you have empty examples:

 $ ​​rspec
 ..**
 
 Pending: (Failures listed here are expected and do not affect your suite's
 status)
 
  1) A cup of coffee with milk is light in color
  # Not yet implemented
  # ./spec/coffee_spec.rb:34
 
  2) A cup of coffee with milk is cooler than 200 degrees Fahrenheit
  # Not yet implemented
  # ./spec/coffee_spec.rb:35
 
 
 Finished in 0.00125 seconds (files took 0.07577 seconds to load)
 4 examples, 0 failures, 2 pending

The two empty examples are marked with yellow asterisks on the progress bar and are listed as “Pending’’ in the output.

Marking Incomplete Work

When you’re sketching out future work, you may actually have an idea of what you want the body of the spec to look like. It’d be nice to be able to mark some expectations as work in progress before you commit so that you’re never committing a failing spec suite.

RSpec provides the pending method for this purpose. You can mark a spec as pending by adding the word pending anywhere inside the spec body, along with an explanation of why the test shouldn’t pass yet. The location matters; any lines before the pending call will still be expected to pass. We typically add it at the top of the example:

 it​ ​'is light in color'​ ​do
  pending ​'Color not implemented yet'
 expect​(coffee.color).to be(​:light​)
 end
 
 it​ ​'is cooler than 200 degrees Fahrenheit'​ ​do
  pending ​'Temperature not implemented yet'
 expect​(coffee.temperature).to be < 200.0
 end

RSpec will run the body of the spec and print the failure so you can see it. But it won’t mark the spec, or your overall suite, as failing:

 $ ​​rspec
 ..**
 
 Pending: (Failures listed here are expected and do not affect your suite's
 status)
 
  1) A cup of coffee with milk is light in color
  # Color not implemented yet
  Failure/Error: expect(coffee.color).to be(:light)
 
  NoMethodError:
  undefined method ‘color’ for #<Coffee:0x007f83b1199a88
  @ingredients=[:milk]>
  # ./spec/coffee_spec.rb:36:in ‘block (3 levels) in <top (required)>’
 
  2) A cup of coffee with milk is cooler than 200 degrees Fahrenheit
  # Temperature not implemented yet
  Failure/Error: expect(coffee.temperature).to be < 200.0
 
  NoMethodError:
  undefined method ‘temperature’ for #<Coffee:0x007f83b11984d0
  @ingredients=[:milk]>
  # ./spec/coffee_spec.rb:41:in ‘block (3 levels) in <top (required)>’
 
 Finished in 0.00161 seconds (files took 0.07898 seconds to load)
 4 examples, 0 failures, 2 pending

Of course, you could just comment out the examples instead of marking them as pending. But unlike commented-out code, pending examples still run and report their failures—which means you can use this information to drive your implementation.

Define an inspect Method for Clearer Test Output

images/aside-icons/info.png

Some failure messages—such as the NoMethodError exceptions printed for these pending coffee specs—include string representations of your objects. Ruby (and RSpec) generate this string by calling inspect on each object.

If a particular class doesn’t define an inspect method, the resulting string will be something like #Coffee:0x007f83b11984d0 @ingredients=[:milk]. To make the output a little more programmer-friendly, we recommend defining this method to print a nice, readable string such as #Coffee (with milk).

Completing Work in Progress

One of the nice things about marking examples as pending is that RSpec will let you know when they start passing.

Let’s see what that looks like. Implement the missing color and temperature methods inside the Coffee class:

 def​ color
  ingredients.include?(​:milk​) ? ​:light​ : ​:dark
 end
 
 def​ temperature
  ingredients.include?(​:milk​) ? 190.0 : 205.0
 end

Now, try rerunning your specs:

 $ ​​rspec
 ..FF
 
 Failures:
 
  1) A cup of coffee with milk is light in color FIXED
  Expected pending ’Color not implemented yet’ to fail. No error was
  raised.
  # ./spec/coffee_spec.rb:42
 
  2) A cup of coffee with milk is cooler than 200 degrees Fahrenheit FIXED
  Expected pending ’Temperature not implemented yet’ to fail. No error
  was raised.
  # ./spec/coffee_spec.rb:47
 
 Finished in 0.00293 seconds (files took 0.08214 seconds to load)
 4 examples, 2 failures
 
 Failed examples:
 
 rspec ./spec/coffee_spec.rb:42 # A cup of coffee with milk is light in color
 rspec ./spec/coffee_spec.rb:47 # A cup of coffee with milk is cooler than
 200 degrees Fahrenheit

RSpec has marked the test suite as failing, because we have examples marked as pending that are actually implemented now. When you remove the pending bits, the entire suite will pass.

If you really don’t want the body of the spec to run at all, you can use skip in place of pending. Or you can use xit, which is a temporary annotation like fit except that it skips the example instead of focusing it.

Use pending to Flag Errors in Third-Party Code

images/aside-icons/info.png

If your spec is failing because of a bug in a dependency, mark it with pending and the ticket ID from their bug tracker; for example, pending ’waiting for a fix for Hitchhiker’s Guide bug #42’. When you later update to a version containing the fix, RSpec will tell you.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset