Branch coverage for Ruby is finally on the horizon! The built-in coverage library is expected to ship in Ruby 2.5 with branch and method coverage options.
And a pure-Ruby gem is in development, too: DeepCover.
I gave DeepCover a try with my main project, the Wiki Education Dashboard, and the coverage of the Ruby portions of the app dropped from 100% to 96.75%. Most of that is just one-line guard clauses like this:
[ruby]return unless Features.wiki_ed?[/ruby]
But there are a few really useful things that DeepCover revealed. First off, unused scopes on Rails ActiveRecord models:
[ruby]
class Revision < ActiveRecord::Base
scope :after_date, ->(date) { where(‘date > ?’, date) }
end
[/ruby]
Unlike default line coverage, DeepCover showed that this lambda wasn’t ever actually used. It was dead code that I removed today.
Similarly, DeepCover shows where default arguments in a method definition are never used, like this:
[ruby]
class Article < ActiveRecord::Base
def update(data={}, save=true)
self.attributes = data
self.save if save
end
end
[/ruby]
This method was overriding the ActiveRecord update
method, adding an option to update without saving. But we no longer use that second argument anywhere in the codebase, meaning I could delete the whole method and rely on the standard version of update
.
Very cool to see and I believe with lambda coverage you are going even further, but related to the pure Ruby implementation might be interested to look at the pure Ruby coverage I implemented in Coverband https://github.com/danmayer/coverband
Thanks! I’d like to try Coverband out some time.
DeepCover co-author here. Thanks for the great examples. The unused scope is probably fairly typical. You mention unused default arguments (which DeepCover can detect), but in the update case, I imagine that the default argument was always used (which we should detect) and that it’s actually DeepCover’s branch coverage that alerted you to the face that `if save` was always true, right?
I think it’s that the default argument was always used, but it was detecting that nothing *except* the default argument was used. I’m not completely sure, though.
The `def` line is the one that was marked as uncovered. (I did this via simplecov, so I didn’t get the visualization of the uncovered parts within the line.)