simplecov: a wrapper on Ruby's own coverage library, with its formatters absorbed and its config API mid rewrite
Code coverage for Ruby with a powerful configuration library and automatic merging of coverage across test suites
At a glance
- What is it?
- SimpleCov does not gather coverage data, Ruby's built-in Coverage library does. What the gem adds is merging across test suites, a configuration vocabulary built on verbs, and one example block that stops mid-word.
- Who is it for?
- SimpleCov is the default answer for Ruby coverage for a good reason: it does the merging and the reporting and leaves collection to the interpreter, so the same two lines work under RSpec, Minitest and Cucumber. Three things to check in your own setup. `SimpleCov.start` has to run before any application code is required, and Spring and other reloading tools will break that ordering quietly.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Ruby, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- simplecov-ruby/simplecov4,929 ★ · +3
| Repository | 2026-09-23 | 2026-11-13 |
|---|---|---|
| simplecov-ruby/simplecov | 4.9k | 4.9k |
3 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Collection is Ruby's job, SimpleCov's job is the verbs around it
The first line of the README settles a common misunderstanding. SimpleCov is a coverage analysis tool for Ruby that uses Ruby's built-in Coverage library to gather the data, and its contribution is processing: a clean API to filter, group, merge, format and display the results. What you get instead is that collecting coverage is a two-line affair, because the hard part is deciding what to do with it. The API is organised around a small set of verbs, formatters are picked by name, thresholds live in a per-criterion `coverage` block where scope is a uniform `per:` argument, and misses can be capped as absolute counts rather than ratios. Configuration goes in the start block, or in a `.simplecov` file at the project root when several test suites share it. That last point is the one to internalise before designing thresholds, because a shared config file is what lets independent suites converge on one report.
Merging is what makes a single report worth reading
In most projects you want overall coverage spanning every kind of test, unit and integration together, and SimpleCov handles that automatically by caching and merging results as it generates reports. That single sentence is the feature the project's own description leads with, automatic merging of coverage across test suites, and it is what separates a report that reflects your whole suite from one that reflects whichever files the last command happened to load. Two consequences follow. First, a report has to be read as a merge, so a low number in one group is a claim about the whole suite rather than about one test file. Second, and less obvious, an unrequired file is invisible unless you ask for it. The configuration has a `cover` directive for exactly that case, reporting on files even if they are never loaded, paired with `skip` to leave things out. A library with a `lib/` directory that nothing requires will therefore show as absent rather than as uncovered, which is the difference between a gap and a blind spot.
The formatters you used to install separately now ship inside the gem
Two formatters are bundled: the default HTML one that renders the browsable report, and a JSON one. Both were once separate gems, `simplecov-html` and `simplecov_json_formatter`, and they are now part of SimpleCov and configured automatically when you launch it. That is a packaging decision with a practical edge. A Gemfile that still names either gem is carrying a dependency whose functionality the main gem already provides, and anyone upgrading will find the old pin is no longer the thing producing the report. A wide variety of alternate formatters are distributed as gems and documented separately, which is how you get output for a different consumer without changing how coverage is collected, since formatters are selected by name. The repository also carries an `html_frontend/` directory, so the HTML report's front end is part of this repository rather than an asset pulled in at build time, which is why the report's behaviour can change with the gem version.
Start before your application code, and beware anything that reloads
The setup is short, and the ordering rule is the part people get wrong. Add the gem without requiring it, then require it and start it at the very top of your test helper, before anything else:
gem 'simplecov', require: false, group: :testrequire 'simplecov'
SimpleCov.start
# Previous content of test helper now starts hereWhich helper you use does not matter, since the same two lines work for `test/test_helper.rb`, `spec/spec_helper.rb`, `rails_helper.rb` and Cucumber's `features/support/env.rb`. What matters is that `SimpleCov.start` runs before any application code is required, because otherwise the underlying Coverage library cannot track those files. The README names the tool that breaks this most often, Spring, which keeps the application loaded between runs. There is a second case with the same shape: when you test a server process such as a JSON API from a separate test process, SimpleCov has to run in the server process to see what that process executes. For Rails the README puts that near the top of `bin/rails`, below the shebang and after `config/boot` is required:
if ENV['RAILS_ENV'] == 'test'
require 'simplecov'
SimpleCov.start 'rails'
endThe configuration example stops mid-word at coverage :branc
The configuration block the README presents is the fastest way to understand the vocabulary, and it also shows where the example breaks off:
SimpleCov.start do
enable_coverage :branch # track branches as well as lines
cover "{app,lib}/**/*.rb" # report on these files, even if never loaded
skip "app/legacy" # ...but leave these out
group "Models", "app/models" # organize the report into groups
coverage :line do
minimum 90 # fail the suite below 90% line coverage
maximum_drop 1 # ...or when coverage drops more than 1%
maximum_missed 5, per: :file # no file may carry more than 5 uncovered lines
end
coverage :branc`enable_coverage :branch` turns on branch tracking, `cover` and `skip` set the file scope, and `group` organises the report. The threshold block is the part worth copying: an absolute floor, a ceiling on how much coverage may drop, and a cap on uncovered lines per file rather than per project, which is the criterion that actually catches a single neglected file. Then the block breaks off at `coverage :branc`, so the branch criterion is announced and not given. Everything around it is complete, including the migration story: legacy spellings still work, they warn and name their replacement, and `deprecations :raise` turns any old spelling that reappears into an error.
track_tests separates code that loaded from code that was tested
Coverage normally answers whether a line ran, not what ran it, and `track_tests` records the second half. RSpec examples and Minitest tests are wrapped automatically. In the HTML report, covered lines that no test executed, lines that only ran at load time or during suite setup, drain to a distinct tint, so code that is merely loaded stops looking like code that is tested, and clicking a line's badge lists the tests that cover it. The same data is available from the terminal, one test id per line and nothing else, so it pipes straight into a runner:
$ simplecov tests lib/simplecov/result.rb:42
spec/result_spec.rb:42`simplecov tests --redundant` inverts the question and lists the tests whose covered lines other tests also cover, which is where a test-pruning session starts. The README is honest that recording has a real cost, which is why the feature is opt-in and comes with levers to control it. The same command surface handles opening the report: `simplecov open` picks the platform opener, `open` on macOS, `xdg-open` on Linux and BSD, `start` on Windows, with `--report PATH` for a non-default location.
Dead code detection runs against a live process, and the section ends mid-command
The dead-code feature is the one with the most operational care behind it. SimpleCov can measure production code usage, described as the surest way to find dead code. The old trick was to plant a log line in a suspect method and watch production for a while. Oneshot coverage runs that experiment for every line at once: a line reports its first execution and nothing after, so a live process records what real traffic uses with the least possible impact on performance. The repository's own configuration shows the culture around the tool: a `.simplecov` file and a `.simplecov_baseline.yml` so the project gates itself, a `.mutant.yml` beside a mutation testing workflow badge, a `Steepfile` with a `sig/` directory beside a typecheck badge, and both `.rubocop.yml` and `.standard.yml` configured side by side. The release line moved three times in September 2026, v1.3.0, v1.3.1 and v1.3.2, with the last push on 2026-10-29. MIT licensed, with `LICENSE` at the root.
Editorial conclusion
SimpleCov is the default answer for Ruby coverage for a good reason: it does the merging and the reporting and leaves collection to the interpreter, so the same two lines work under RSpec, Minitest and Cucumber. Three things to check in your own setup. `SimpleCov.start` has to run before any application code is required, and Spring and other reloading tools will break that ordering quietly. The two formatters most people installed separately are now inside the gem, which changes what your Gemfile.lock means when you upgrade. And the configuration API is in a transition: legacy spellings still work and warn, and `deprecations :raise` turns the warnings into failures, so a good time to migrate is before you pin a CI gate. Three releases landed between 2026-09-12 and 2026-09-30, v1.3.0 through v1.3.2, so read the changelog rather than assuming a stable configuration surface. MIT licensed.
Frequently asked questions
What alternatives to simplecov exist for report formats?
The HTML and JSON formatters are now bundled into SimpleCov itself, having previously been separate gems called `simplecov-html` and `simplecov_json_formatter`. For other output the project points to a set of alternate formatters distributed as gems and documented separately, selected by name in the configuration.
How do you set up SimpleCov in a Ruby project?
Add `gem 'simplecov', require: false, group: :test` to the Gemfile, then require and start SimpleCov at the very top of your test helper with `require 'simplecov'` followed by `SimpleCov.start`. The same two lines work for RSpec, Minitest and Cucumber, but the start must run before any application code is required.
How do you get SimpleCov to measure a Rails server process?
SimpleCov has to run in the process you want to analyze. When a separate test process drives a server, the README puts the require and `SimpleCov.start 'rails'` near the top of `bin/rails`, below the shebang and after `config/boot` is required, guarded by a test environment check.
What thresholds can SimpleCov enforce?
Thresholds live in a per-criterion `coverage` block with a uniform `per:` argument. The example enforces a minimum of 90 for lines, a `maximum_drop` of 1, and `maximum_missed 5, per: :file`, so no single file may carry more than five uncovered lines.
Can SimpleCov tell you which test covers a line?
Yes, with `track_tests`, which wraps RSpec examples and Minitest tests automatically. Lines that only ran at load time drain to a distinct tint in the HTML report, clicking a line's badge lists the tests covering it, and `simplecov tests path/file.rb:42` prints one test id per line while `--redundant` lists tests covered by other tests.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/simplecov-ruby-simplecov)Star history chart for your README
[](https://hysenlabs.com/projects/simplecov-ruby-simplecov)Was this useful?