Formatters and output
August 26, 2026 · View on GitHub
The bundled HTML and JSON formatters, third-party formatters, the coverage.json schema, and output diagnostics.
Part of the SimpleCov documentation.
Formatters
HTML report appearance
The bundled HTML formatter produces a self-contained report with file-list and source-file views. Its Light/Dark and Colorblind controls apply to both views and remember their settings in the browser.
Light mode
| File list | Source file |
|---|---|
Dark mode
| File list | Source file |
|---|---|
Colorblind mode
Colorblind mode swaps covered and missed for blue and orange, the pairing red/green colour vision cannot separate, and applies the same swap to the coverage bands.
| File list | Source file |
|---|---|
Both together
The two controls are independent, so colorblind mode carries its blue/orange palette into dark mode as well.
| File list | Source file |
|---|---|
Using your own formatter
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
SimpleCov.result.format! instantiates a configured formatter class, then calls #format(result), where result is a
SimpleCov::Result. A ready-built formatter instance receives #format directly, which lets constructor options carry
through to report generation. Do whatever you wish with it.
Passing options to formatters
Anywhere a formatter class is accepted, a ready-built instance works too — that's how you reach constructor options.
The built-in HTML and JSON formatters take silent: true to suppress the "Coverage report generated" status line on
stderr, and output_dir: to write the report somewhere other than SimpleCov.coverage_path:
SimpleCov.start do
formatter SimpleCov::Formatter::HTMLFormatter.new(silent: true)
end
Instances mix freely with classes in formatters lists as well.
Using multiple formatters
The bundled formatters are named by symbol, so the common combinations need no constants at all:
SimpleCov.start do
formats :html, :json
end
:html, :json, :simple, and :baseline are the built-in names. Other formatters ship as separate gems you'll
need to add and require — for example, simplecov-cobertura for the
Cobertura XML that many CI services consume — and their classes (or ready-built instances, for constructor options)
mix freely beside the names:
require "simplecov-cobertura"
SimpleCov.start do
formats :html, SimpleCov::Formatter::CoberturaFormatter
end
The constant-spelled form remains equivalent:
SimpleCov.formatters = [
SimpleCov::Formatter::HTMLFormatter,
SimpleCov::Formatter::CoberturaFormatter,
]
JSON formatter
SimpleCov::Formatter::JSONFormatter emits JSON — useful for CI consumption or reporting to external services.
SimpleCov.formatter = SimpleCov::Formatter::JSONFormatter
By default coverage.json carries the full source-text array for every file, which makes the payload self-contained
but dominates the file size on larger projects. Tools that read the project's source files directly from disk can opt
out of that field with:
SimpleCov.start do
source_in_json false
end
The HTML report always retains the source array in its embedded data — the client-side viewer renders source from
there. The setting only affects the side-file coverage.json. When the source is omitted, meta.commit (the git
commit SHA the report was generated against) lets tools recover the exact source lines from repository history.
The JSON formatter was originally a separate gem, simplecov_json_formatter. It is now built in and loaded by default; existing code that does
require "simplecov_json_formatter"will continue to work.
Baseline formatter
SimpleCov::Formatter::BaselineFormatter auto-ratchets the
per-file coverage baseline at the end of every run, for teams that want
floors to tighten continuously instead of by deliberate simplecov ratchet invocations:
SimpleCov.start do
formats :html, :baseline
end
The semantics are exactly the CLI's: the first run generates .simplecov_baseline.yml with a floor for every reported
file, later runs raise the floors of files that improved, keep the floors of files that regressed (naming them in the
status line), prune entries for deleted files, and never add entries for new files. The file is rewritten only when a
floor actually moved, so a run that changes nothing leaves the working tree clean, and the exit checks still judge the
run against the floors as they were when it started. The trade-off of the auto mode is that floors tighten without a
human in the loop. The diff still lands in the working tree for the next commit to carry.
JSON Schema for coverage.json
coverage.json is a public contract, described by a JSON Schema (2020-12) so downstream tools can validate it,
generate types, or pin to a known shape. Every emitted document carries a top-level $schema URL pointing at the
versioned canonical, plus a human-readable meta.schema_version ("major.minor").
The versioned canonical lives at schemas/coverage-v1.3.schema.json and
long-lived integrations should pin to it. Once a SimpleCov release ships with a given versioned schema file, that file
is immutable: bug fixes, additions, or shape changes ship as a new versioned file (a minor or major bump), never as a
silent rewrite of an already-released one. Schemas may still be corrected in-place between gem releases — i.e., the
schema file as it currently exists on main may change before the next gem release, but the schema for any published
gem version stays frozen. A convenience alias at schemas/coverage.schema.json always
tracks the latest and may shift when a new SimpleCov release bumps the schema.
The schema version is independent of the gem version:
- Additive changes (new fields) bump the minor segment. Existing consumers keep working.
- Removals or shape changes bump the major segment, and ship as a new
schemas/coverage-vX.0.schema.jsonfile so v1.x consumers stay valid.
The current version is 1.3. 1.1 added the optional per-test context data recorded under
track_tests: a document-level contexts array of ids and,
per file, hex bitmaps of the lines each context executed, present exactly when the formatted result carried a
complete context map. 1.2 added the optional history array (the recorded run history with this run appended). 1.3
added the optional production section: the window and per-file lines a
production coverage store accumulated, present exactly when
production_coverage names a readable store at report
time. Top-level structure:
{
"$schema": "https://raw.githubusercontent.com/simplecov-ruby/simplecov/main/schemas/coverage-v1.3.schema.json",
"meta": { /* schema_version, simplecov_version, command_name, project_name, timestamp, root, commit, line_coverage, branch_coverage, method_coverage */ },
"total": { /* aggregate stats for lines (and branches / methods when enabled) */ },
"coverage": { "<project-relative path>": { /* per-file lines, source, branches, methods, contexts, etc. */ } },
"contexts": [ /* recorded context ids (with track_tests: test definition locations); present only when recorded */ ],
"history": [ /* recorded runs, oldest first, this run last; present only when past runs are recorded */ ],
"production": { /* window + per-file lines and last_seen stamps from a production store; present only when configured */ },
"groups": { "<group name>": { /* per-group stats + files */ } },
"errors": { /* minimum_coverage, minimum_coverage_by_file, minimum_coverage_by_group, maximum_coverage, maximum_coverage_drop violations */ }
}
The .resultset.json file is not schema'd — it's SimpleCov-internal and may change shape across releases. Build
integrations on top of coverage.json.
More formatters, editor integrations, and hosted services
- Open Source formatter and integration plugins for SimpleCov
- Editor Integration
- Hosted (commercial) services
Output and diagnostics
Errors and exit statuses
If an error is raised, SimpleCov prints a message to STDERR with the exit status, to aid debugging:
SimpleCov failed with exit 1
Disable this message with:
SimpleCov.print_errors false
Color output
When color is enabled, SimpleCov highlights coverage percentages in its STDERR diagnostics by band (green for
>= 90%, yellow for >= 75%, red below) and prints the "SimpleCov failed with exit ..." summary in red. By default,
color is on only when STDERR is a TTY. Two environment variables override that:
NO_COLOR=1(any non-empty value) disables color even when stderr is a TTY. Honors the no-color.org convention.FORCE_COLOR=1(any non-empty value) enables color even when stderr is not a TTY. Useful when stderr is piped through a wrapper that itself renders ANSI in a terminal (parallel_tests --combine-stderr, log multiplexers, some CI runners).
NO_COLOR wins if both are set.
For programmatic control, use SimpleCov.color. An explicit true or false wins over the env vars and TTY detection:
SimpleCov.color true # always on
SimpleCov.color false # always off
SimpleCov.color :auto # default behavior: NO_COLOR/FORCE_COLOR/TTY