Ruby Behavior Driven Development Tutorial

Exercise 7: Code Quality Metrics

by Richard Kuehnel

This exercise covers the following topics.

  1. writing RDoc comments for improved code documentation
  2. configuring SimpleCov to measure code coverage
  3. writing Cucumber and RSpec support files
  4. code execution order for Cucumber and RSpec
  5. the importance of business logic delegation
  6. measuring code quality using Reek

Updating the Project Documentation

Just for practice, create a new Dogbowl class by inserting the following code and RDoc comments into a new file lib/statowl/dogbowl.rb:

#
# A Dogbowl is the center of the canine universe.
# It can hold dog food, dog biscuits, or water.
#
# Dogbowl has the following methods:
# fill_with_biscuits:: fill an empty dogbowl with dog biscuits
# empty_and_clean:: empty the dogbowl and clean it for tomorrow
#
class Dogbowl

  #
  # Fill with dog biscuits
  #
  # @param biscuit_name name of the dog biscuit
  # @param bowl_id ID of the bowl to fill with biscuits
  # @return true if successful, otherwise false
  #
  def fill_with_biscuits(biscuit_name, bowl_id)
    true
  end

  #
  # Empty and clean dogbowl
  # @param bowl_id ID of the bowl
  # @return true if successful, otherwise false
  #
  def empty_and_clean(bowl_id)
    true
  end
end

Write similar comments for your extensions to the Array class. Execute rake yard and bin/webrick and browse your project home page.

  1. Notice how the "Method list" link in the upper right also shows the class that contains each method. Follow the links to read the source code for the median method.
  2. Compare the RDoc comments in your source code with the resulting text in the corresponding RDoc page. To reduce unnecessary clutter, observe that your raw comments are not included when you click on "View source."
  3. At the top of the page, click on "File List" and then "README." Click on the "Cucumber Test Results" link at the bottom of the page. All green?

SimpleCov Test Coverage

It is a good idea to eliminate duplication by inserting boilerplate test code into test helpers. In Cucumber this is accomplished by putting the code into files in the features/support directory. All of the files in this directory are executed before any scenarios are tested. If the file features/support/env.rb exists, then it is executed before any other helper files.

RSpec follows a similar convention. It executes the file spec/spec_helper.rb, if it exists, before any unit tests are run.

You will use test helpers for SimpleCov, a simple code coverage tool. Once started, it keeps track of the lines of production code that are executed during tests. To use SimpleCov with RSpec, insert the following into a new file spec/spec_helper.rb:

require 'simplecov'

SimpleCov.coverage_dir('doc/rspec-coverage')
SimpleCov.start

Add a require for it in your Rakefile:

desc 'Run RSpecs'
RSpec::Core::RakeTask.new do |t|
  t.rspec_opts = ["--color", "--format", "documentation", "-r ./spec/spec_helper"]
end

Add a development dependency for 'simplecov' to your statowl.gemspec file.

To use SimpleCov with Cucumber, insert the same helper lines of code into features/support/env.rb:

require 'simplecov'

SimpleCov.coverage_dir('doc/rspec-coverage')
SimpleCov.start

Now add the following just below the "Cucumber Test Results" link in README.doc:

== Testing

This project uses RSpec and Cucumber for Behavior Driven Development.

{Cucumber Test Results}[link:cuke_results.html]

{RSpec Code Coverage Results}[link:rspec-coverage]
...

Run your tests and build your documentation:

rake spec
rake features
rake yard

Run bin/webrick and click on the "RSpec Code Coverage Results" link.

  1. Is the Dogbowl class included? What can you conclude about completely untested code?
  2. How is your RSpec code coverage for the mean, median, and variance methods? (Click on the link to "extensions.rb.")
  3. How about the standard_deviation method that was tested via Cucumber?

Notice how poor the coverage looks for methods that were invoked via bin/statowl. SimpleCov does a great job with unit tests and acceptance tests of Ruby code libraries. It is limited, however, in its ability to measure coverage on code that is executed indirectly, such as from the command line or from a browser. As noted earlier, it is a good idea to keep command line executables lean by delegating business logic to an API that can be independently tested, just as you do with mean, median, variance, and standard_deviation.

In a later exercise, when you write a RESTful web service for statistics computation, you will observe a similar principle: the service itself should simply parse the HTTP request and send the results back to the client browser. All business logic should be delegated to code that can be independently tested.

Reek Quality Metrics

Reek is a code quality metrics tool for Ruby. It is similar to Java's Checkstyle or Findbugs. Its most remarkable innovation over Java is humor. This is Ruby after all! It's official label on GitHub is

Code smell detector for Ruby

To add Reek to your project, all you need is to install the gem. Then execute reek lib to check all of the Ruby code in the lib directory.

Try it out and then correct any "smells" that you care about.

Exercise Summary

In this exercise you wrote RDoc comments to improve your code documentation. You used test helper files to configure SimpleCov to measure code coverage. You also noted the importance of keeping business logic out of the command line interface code. Finally, you used Reek to identify code smells and improve code quality.

In the next exercise you will learn how to apply Behavior Driven Development to RESTful web services.