Configuring Code Coverage for Ruby/Rails project

Getting Started

SimpleCov is a code coverage analytical tool for Ruby 1.9 built as ruby gem.

In order to use it in your project add this gems to your Gemfile:

  group :coverage do
    gem 'simplecov', :require => false
  end

The formatter for SimpleCov is packaged as a separate gem called simplecov-html. You don’t have to include it explicitly - SimpleCov already has it as a dependency.

You don’t have to call coverage as separate command. Instead, you have to integrate it with your specs or tests. How to do it?

You have to start SimpleCov before any of your specs/tests in order to be covered:

# spec_helper.rb
if ENV['COVERAGE'] == 'true'
  require 'simplecov'

  SimpleCov.start
end
# my_model_spec.rb
require 'spec_helper'

describe MyModel do
...
end

Now you can run your specs:

rspec spec/my_model_spec.rb

Coverage report will be saved in coverage folder. Open up coverage/index.html in your browser and check out results:

open coverage/index.html

If you want to save results to another folder, change coverage_path property:

SimpleCov.coverage_path = 'my_output'

In real project you have different groups of specs executed by separate commands. If you want to merge coverage results generated by each of command into total report, you have to assign unique command name for each group of specs:

# model_spec_helper.rb
if ENV['COVERAGE'] == 'true'
  require 'simplecov'

  SimpleCov.command_name "rspec:models"

  SimpleCov.start unless SimpleCov.running
end
# domains_spec_helper.rb
if ENV['COVERAGE'] == 'true'
  require 'simplecov'

  SimpleCov.command_name "rspec:domains"

  SimpleCov.start unless SimpleCov.running
end

SimpleCov Adapters

SimpleCov understands notion of adapter. You can start SimpleCov with preconfigured rails adapter:

SimpleCov.start 'rails'

Let’s look closer at definition of rails adapter:

SimpleCov.adapters.define 'rails' do
  ...
  add_filter '/config/'
  add_filter '/db/'
  add_filter '/vendor/bundle/'

  add_group 'Controllers', 'app/controllers'
  add_group 'Models', 'app/models'
  add_group 'Mailers', 'app/mailers'
  add_group 'Helpers', 'app/helpers'
  add_group 'Libraries', 'lib'
  add_group 'Plugins', 'vendor/plugins'
end

As you can see, adapter definition includes filters and groups. Filters specify which directories and files will be includes into coverage. Groups define separate tabs of coverage report.

You can read more about groups and filters on SimpleCov home page.

If you are not satisfied with rails adapter, you can create your own:

  def register_adapter adapter_name
    SimpleCov.adapters.define adapter_name do
      add_filter '/spec/'
      add_filter '/db/'
      add_filter 'lib/tasks/'
      add_filter '/vendor/'
      add_filter '/config/'

      add_group "Domain", "app/domain"
      add_group "Models", "app/models"
      add_group "Services", "app/services"
    end
  end

Full Example

Let’s create compete example that can be used in any project:

# spec_helper.rb
  def init_spec command_name
    ...
    SimplecovHelper.run_simplecov command_name,
      "your_adapter_name" if ENV['COVERAGE'] == 'true'
    ...
  end

And this is SimpleCov helper implemented as utility module:

module SimplecovHelper
  extend self

  def run_simplecov command_name, adapter_name
    require 'simplecov'

    SimpleCov.command_name command_name

    start_simplecov(adapter_name) unless SimpleCov.running
  end

  private

  def start_simplecov adapter_name
    register_adapter adapter_name

    SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter

    SimpleCov.at_exit do
      SimpleCov.result.format!
    end

    SimpleCov.start adapter_name
  end

  def register_adapter adapter_name
    ...
    # see implementation in previous section
  end
end

Alternatives

Another gem dedicated to code coverage is coco gem.

It provides simple code coverage report and it’s much easier to configure, but resulting report is much simpler and less convenient.

You can choose what tool to use in your project based on your preferences.

If you need to cover your javascript code, you can visit my previous blog article.