Back to Scaling Ruby Applications guides

Getting Started with SimpleCov for Ruby

Stanley Ulili
Updated on October 31, 2025

SimpleCov is a code coverage analysis tool for Ruby applications that provides detailed insights into which parts of your codebase are executed during test runs. Its comprehensive reporting capabilities and seamless integration with popular testing frameworks have made it an essential tool for maintaining high-quality, well-tested applications.

This guide will walk you through setting up code coverage tracking for your Ruby application using SimpleCov. You'll discover how to take advantage of the gem's extensive features and tailor them to create an optimal configuration that matches your project's specific requirements.

Prerequisites

Before moving forward with this guide, make sure you have Ruby installed on your development machine (version 2.7 or higher is recommended). This tutorial also assumes you're comfortable with Ruby's basic testing concepts and have experience with at least one testing framework like RSpec, Minitest, or Cucumber.

Getting started with SimpleCov

To get the most value from this tutorial, create a fresh Ruby project where you can experiment with the concepts we'll be exploring. Begin by setting up a new project using these commands:

 
mkdir simplecov-demo && cd simplecov-demo
 
bundle init

Next, add SimpleCov to your Gemfile by running this command:

 
bundle add simplecov --group development,test

The examples in this guide are compatible with SimpleCov 0.22.x, which is the latest major version at the time of writing. Create a new lib/calculator.rb file in your project directory with this simple class:

 
mkdir lib
lib/calculator.rb
class Calculator
  def add(a, b)
    a + b
  end

  def subtract(a, b)
    a - b
  end

  def multiply(a, b)
    a * b
  end

  def divide(a, b)
    raise ArgumentError, 'Cannot divide by zero' if b.zero?
    a / b
  end
end

Now create a test file at spec/calculator_spec.rb:

 
mkdir spec
spec/calculator_spec.rb
require 'simplecov'
SimpleCov.start

require_relative '../lib/calculator'

RSpec.describe Calculator do
  let(:calculator) { Calculator.new }

  describe '#add' do
    it 'returns the sum of two numbers' do
      expect(calculator.add(2, 3)).to eq(5)
    end
  end

  describe '#subtract' do
    it 'returns the difference between two numbers' do
      expect(calculator.subtract(5, 3)).to eq(2)
    end
  end
end

This example demonstrates the minimum configuration needed to start tracking coverage. The critical part is requiring SimpleCov and calling SimpleCov.start before loading any application code. This ensures SimpleCov can properly instrument your code and track execution.

Before running the tests, install RSpec:

 
bundle add rspec
 
bundle exec rspec --init

Now execute your test suite:

 
bundle exec rspec

After the tests complete, you'll see coverage information printed to your terminal:

Output
2 examples, 0 failures

Coverage report generated for RSpec to /path/to/simplecov-demo/coverage.
Line Coverage: 70.0% (7 / 10)

SimpleCov automatically generates an HTML report in the coverage directory. Open coverage/index.html in your browser to see a detailed breakdown of your code coverage:

SimpleCov HTML coverage report showing overall 70% coverage with file listing

The report shows that 70% of the relevant lines in your code were executed during the test run. The summary table displays each file along with its coverage percentage, total lines, and the number of lines covered versus missed.

Clicking on lib/calculator.rb takes you to a detailed view that reveals exactly which lines were covered and which weren't:

SimpleCov detailed file view showing line-by-line coverage with hit counts

The numbers displayed next to each line indicate how many times that line executed during the test run. Lines that executed at least once show their hit count, while untested lines are clearly marked.

Notice that the multiply and divide methods lack coverage on their implementation lines, clearly identifying the untested code paths. This visual feedback makes it straightforward to spot gaps in your test coverage and prioritize which methods need additional tests.

Configuring SimpleCov properly

While the basic setup works, there's a better way to configure SimpleCov that avoids cluttering your test files with coverage setup code. The recommended approach is creating a dedicated .simplecov file in your project root:

.simplecov
SimpleCov.start do
  add_filter '/spec/'
  add_filter '/test/'
end

This configuration file is automatically loaded by SimpleCov when it starts. With this setup, you can simplify your spec/spec_helper.rb file to just require SimpleCov without explicit configuration:

spec/spec_helper.rb
require 'simplecov'
SimpleCov.start
# Rest of your RSpec configuration RSpec.configure do |config| config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end end

Then update your spec/calculator_spec.rb to use the spec helper instead of requiring SimpleCov directly:

spec/calculator_spec.rb
require_relative 'spec_helper'
require_relative '../lib/calculator' RSpec.describe Calculator do let(:calculator) { Calculator.new } describe '#add' do it 'returns the sum of two numbers' do expect(calculator.add(2, 3)).to eq(5) end end describe '#subtract' do it 'returns the difference between two numbers' do expect(calculator.subtract(5, 3)).to eq(2) end end end

Run your tests again to verify everything still works:

 
bundle exec rspec
Output
2 examples, 0 failures

Coverage report generated for RSpec to /path/to/simplecov-demo/coverage.
Line Coverage: 70.0% (7 / 10)

This approach keeps configuration centralized and makes it easier to maintain consistent coverage settings across your entire test suite. The .simplecov file is also where you'll add most of your customizations as your needs evolve.

Understanding coverage metrics

SimpleCov tracks several types of coverage metrics that provide different insights into how thoroughly your code is tested. The default metric is line coverage, which measures the percentage of executable lines that ran during your test suite.

Line coverage

Line coverage is the most straightforward metric. It measures whether each line of code was executed at least once during the test run. When you see a line marked as covered in the HTML report, it means at least one test caused that line to execute:

 
def process_payment(amount)
  return false if amount <= 0  # Line 1

  charge_card(amount)          # Line 2
  send_receipt                 # Line 3
  true                         # Line 4
end

If your tests only call process_payment with positive amounts, Line 1 executes but the early return doesn't trigger. Lines 2-4 all execute normally. This gives you 100% line coverage even though the error condition isn't fully tested.

Branch coverage

Branch coverage goes deeper than line coverage by tracking whether both sides of conditional statements were executed. This catches situations where a line runs but not all possible paths through that line were tested:

 
def discount_price(price, member)
  member ? price * 0.9 : price
end

Testing only with member = true gives 100% line coverage but only 50% branch coverage because the false branch never executes. Branch coverage helps identify these hidden gaps in your test suite.

To enable branch coverage in SimpleCov, update your .simplecov file:

.simplecov
SimpleCov.start do
enable_coverage :branch
add_filter '/spec/' add_filter '/test/' end

Run your tests again to see branch coverage metrics:

 
bundle exec rspec
Output
2 examples, 0 failures

Coverage report generated for RSpec to /path/to/simplecov-demo/coverage.
Line Coverage: 70.0% (7 / 10)
Branch Coverage: 0.0% (0 / 2)

Branch coverage typically runs lower than line coverage because it's more demanding. You can set separate thresholds for each metric to acknowledge this reality while still maintaining high standards.

Setting coverage thresholds

Establishing minimum coverage requirements prevents coverage from degrading over time as new code gets added. SimpleCov makes it straightforward to enforce these standards by failing your test suite when coverage drops below acceptable levels.

Add coverage thresholds to your .simplecov configuration:

.simplecov
SimpleCov.start do
  enable_coverage :branch

  add_filter '/spec/'
  add_filter '/test/'

minimum_coverage line: 90, branch: 80
end

When coverage falls below the specified thresholds, SimpleCov exits with a non-zero status code and prints a clear error message:

 
bundle exec rspec
Output
2 examples, 0 failures

Coverage report generated for RSpec to /path/to/simplecov-demo/coverage.
Line Coverage: 70.0% (7 / 10)
Branch Coverage: 0.0% (0 / 2)
Line coverage (70.00%) is below the expected minimum coverage (90.00%).
Branch coverage (0.00%) is below the expected minimum coverage (80.00%).
SimpleCov failed with exit 2 due to a coverage related error

This automatic failure is particularly valuable in continuous integration pipelines, where it can block merges of poorly tested code. Your CI system will catch the failure and prevent the code from reaching production.

You can also track coverage changes by comparing against the last run:

.simplecov
SimpleCov.start do
  enable_coverage :branch

  add_filter '/spec/'
  add_filter '/test/'

  minimum_coverage line: 90, branch: 80
maximum_coverage_drop 2
end

The maximum_coverage_drop setting fails the build if coverage decreases by more than 2% compared to the previous run. This catches situations where new code brings down overall coverage even if the total remains above your minimum threshold.

Filtering files from coverage reports

Not all files in your project need coverage tracking. Test files, configuration files, and generated code typically fall outside the scope of meaningful coverage analysis. SimpleCov provides flexible filtering options to exclude these files from your reports.

The add_filter method accepts string patterns or regular expressions to match files you want to exclude:

.simplecov
SimpleCov.start do
  enable_coverage :branch

  # Exclude test directories
  add_filter '/spec/'
  add_filter '/test/'

  # Exclude configuration files
  add_filter '/config/'

  # Exclude database migrations
  add_filter '/db/migrate/'

  # Exclude vendor directory
  add_filter '/vendor/'

  minimum_coverage line: 90, branch: 80
end

Or you can also use block syntax for more complex filtering logic:

.simplecov
SimpleCov.start do
  enable_coverage :branch

  add_filter '/spec/'
  add_filter '/test/'

add_filter do |source_file|
# Exclude files with fewer than 5 lines
source_file.lines.count < 5
end
minimum_coverage line: 90, branch: 80 end

For Rails applications, SimpleCov provides a convenient preset that automatically excludes common directories:

.simplecov
SimpleCov.start 'rails' do
  enable_coverage :branch

  # Additional custom filters
  add_filter '/app/admin/'

  minimum_coverage line: 90, branch: 80
end

The 'rails' preset excludes test/, spec/, config/, and other standard Rails directories, saving you from manually configuring each filter. The coverage report will now only includes files that pass your filter criteria, giving you a cleaner view of your application's actual test coverage.

Final thoughts

SimpleCov makes it easy to measure, understand, and improve your test coverage in Ruby applications. By setting it up once, you gain clear insights into which parts of your code are tested and which need more attention.

With features like branch coverage, coverage thresholds, and customizable filters, SimpleCov helps you maintain reliable, high-quality code and prevents untested code from slipping into production.

Got an article suggestion? Let us know
Next article
Top 10 Ruby on Rails Alternatives for Web Development
Discover the top 10 Ruby on Rails alternatives. Compare Django, Laravel, Express.js, Spring Boot & more with detailed pros, cons & features.
Licensed under CC-BY-NC-SA

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.