Coverage

Starting coverage

Coverage has to start before the code under test is loaded, or the lines are already compiled without the counter. The test file starts it first, then requires the library:

require "coverage" Coverage.start(lines: true) require_relative "../lib/calculator" require "minitest/autorun"

Coverage.start takes the kinds of coverage: lines, branches, methods and oneshot_lines. Line coverage is the cheap default; branch coverage answers the harder question of whether each side of an if ran.

The report

Coverage.result returns a hash keyed by file. Each value has a :lines array with one entry per line: nil for a line that cannot run, zero for a line that did not, a positive count for a line that did. The summary is four lines of arithmetic:

Minitest.after_run do Coverage.result.each do |file, data| next unless file.end_with?("lib/calculator.rb") lines = data[:lines] covered = lines.count { |n| n && n.positive? } total = lines.count { |n| !n.nil? } puts format("%s: %d/%d lines (%.0f%%)", File.basename(file), covered, total, covered * 100.0 / total) end end

The filter on the file name matters: a report that includes the test file and every gem reports a number that means nothing. A project reports the files it owns.

Running it

ruby test/coverage_test.rb

Finished in 0.000829s, 1206.7224 runs/s, 1206.7224 assertions/s. 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips calculator.rb: 3/5 lines (60%)

The test only calls add, so the two lines of divide and its guard are uncovered and the percentage is honest. Add a test for the zero case and the same report moves to 100% — which is the moment to read the next section.

Reading it honestly

What to watch for

Starting coverage after a require misses the code that was already loaded, which is why the order in the first block is not cosmetic.

A coverage run in the same process as a profiler or a reloader measures the tool as well as the code. Run coverage in its own invocation, or the numbers drift between machines.

Next: RSpec in one lesson

Article author: Arthur Isaev