1. Technology
You can opt-out at any time. Please refer to our privacy policy for contact information.

Simple Code Metrics in Ruby with SimpleCov

By

Simple Code Metrics
Simple Code Metrics in Ruby with SimpleCov

This article is part of a series. For more information, see Code Coverage with SimpleCov.

Code coverage is the measure of the total percentage of lines reached by the Ruby interpreter when running a program, typically a test suite. For example, ideal tests have a code coverage of 100%, the tests have at least reached every line in the program (though that doesn't mean they've tested every line, a code coverage library has no way to see that). This is an important metric as you may think you have completely air-tight tests, but a coverage library will tell you if you've really reached every line of code with your tests.

Why is this Important?

Code coverage at first does seem a bit silly. Not only are you testing your program the "primitive" way by simply seeing if it works, you're also testing the output of entire subsystems of your program and raising exceptions, you're also going the extra mile and running unit tests and integration tests and every other kind of test, why do you need code coverage to pile on?

  • Writing tests is hard. OK, writing tests is easy, but writing good tests is hard. A set of unit tests will test each and every branch of every conditional in your code. This can be difficult, and even more difficult to see which branches you've reached and which you haven't. Code coverage at least gives you a rough idea of which branches you've reached, and running individual tests with coverage enabled gives you a snapshot of the lines reached by that test.

  • Maintaining tests is hard. It's easy for things to get lost in the shuffle. When adding a new feature, you may unintentionally break an old test in such a way that it still passes, but doesn't cover the lines you intended it to cover. Or when hastily adding new features, you may unintentionally leave some lines untested (see the first point).

Testing shows that your code works correctly. Code coverage shows that you've tested all of the code.

Installing Simplecov

Simplecov is a simple Ruby gem. You can install it with the typical gem install command.

$ gem install simplecov

Or you can add it to your Gemfile and let bundler do its thing. Add the line gem 'simplecov', :require => false, :group => :test to your Rails Gemfile and run bundle install.

Using Simplecov

Require simplecov and run SimpleCov.start before you require any of your code or even your test suite. After that, everything else should be exactly the same. If you run your tests, you'll get a coverage directory with an HTML file. Open this in your browser to see the results.

For example, I have this simple die.rb which simulates die rolls.


#!/usr/bin/env ruby
# A simple library to simulate the rolling of dice

class Die
  attr_reader :sides 

  def initialize(sides)
    @sides = sides
  end

  def roll
    rand 1..@sides
  end

  class Error < Exception; end

  # Roll an RPG-style "3D6" type roll
  def self.roll(str)
    dat = str.match /(\d+)D(\d+)/i
    if dat.nil?
      raise Die::Error.new("Invalid die roll string")
    end

    total = 0
    num = dat[0].to_i
    die = Die.new dat[1].to_i

    num.times do
      total += die.roll
    end
  end
end

And its test suite (using the awesome Minitest).


#!/usr/bin/env ruby
require 'simplecov'
SimpleCov.start

require 'test/unit'
require './dice'

class TestDieClass < Test::Unit::TestCase
  def test_die_creation
    die = Die.new(6)
    assert die.sides == 6, "Die does not have 6 sides"
  end

  def test_die_roll
    die = Die.new(6)

    1_000.times do
      assert (1..6).include?(die.roll), "Die roll out of range"
    end
  end

  def test_parsing_die_roll
    1_000.times do
      assert (1..6).include?(Die.roll("1D6")), "Parsed die roll out of range"
    end
  end
end

Notice the two SimpleCov lines at the top of the test suite. When I run this, I'll get an output like so (relevant lines in bold).


# Running tests:

...

Finished tests in 0.022788s, 131.6482 tests/s, 87809.3734 assertions/s.

3 tests, 2001 assertions, 0 failures, 0 errors, 0 skips
Coverage report generated for Unit Tests to ./coverage. 15 / 16 LOC (93.75%) covered.

It says right there my overall code coverage score, 93.75%, but there's so much more. If you open up coverage/index.html, you'll see a full report of the coverage of each individual file, and if you click on the file, you'll see a report like the screenshot at the top of the article. It shows you covered lines in green, uncovered lines in red. This is extremely useful. As you can see, we haven't covered the condition of an invalid die roll string, so let's add a test for that.


  def test_invalid_die_roll
    assert_raises(Die::Error) {  Die.roll("invalid")  }
  end

If you then re-run the tests, you can see that the final uncovered line is now covered, and code coverage has raised to 100%.

More?

This is just the very basics of SimpleCov. In the next article, we'll look at a more detailed example, as well as using a Rails project to deal with multiple files, groupings of files and filters. While certainly not as involved as a test suite, a coverage library is certainly something you really should get familiar with, as just like tests they inform you of problems before they become problems.

  1. About.com
  2. Technology
  3. Ruby
  4. Gems
  5. SimpleCov
  6. Simple Code Metrics

©2014 About.com. All rights reserved.