In the previous two posts, we looked at extending Minitest with new Assertions and Expectations, and modifying the progress reporter output. In this post, we will look at what can be done with the data collected from the test suite once it’s run.
To be clear, Minitest has a class called
SummaryReporter
which has the purpose of summarizing the run, providing information on the
arguments used, displaying failures and errors, and reporting the statistics
from the run. The only way to alter this output is to override the
SummaryReporter
.
In this post, rather than altering Minitest itself, we’ll look at what can be
done with the data collected from the test suite. We’ll use a plugin called
VocalReporter
as our example.
All custom reporters will be a subclass of
AbstractReporter,
either by subclassing it directly or by inheritance through another reporter
such as StatisticsReporter
. In so doing, your class will be able to override,
the following four methods:
start
- Called when the run has startedrecord
- Called for each result, passed or otherwisereport
- Called at the end of the runpassed?
- Called to see if you detected any problemsFor our example, we will be subclassing the
StatisticsReporter
and only use the report
method. We’ll also take advantage of the Mac’s say
command to announce our test results.
module Minitest
def self.plugin_vocal_reporter_init(options)
self.reporter << VocalReporter.new(options[:io], options)
end
class VocalReporter < StatisticsReporter
def report
super
pct = self.failures / self.count.to_f * 100.0
if pct > 50.0
message = "#{self.count} tests run with #{self.failures} failures. Are
you even trying?"
elsif self.failures > 0
message = "#{self.count} tests run with #{self.failures} failures. That
kinda sucks"
else
message = "%d tests run with no failures. You rock!" % self.count
end
`say #{message}`
end
end
end
As explained in the previous post under “must_respond_to :conventions”, the file’s name and init methods must be in alignment.
Let’s break this down:
VocalReporter
to Minitest’s
CompositeReporter
which handles execution of the four methods (#start
, #record
, #report
,
and #passed?
)across all reportersVocalReporter
class and inheriting from the
StatisticsReporterreport
method which will output our resultsStatisticReporter
’s #report
method in order to get access
to attributes set therein, such as #failures
and #count
If you were to include this plugin in your test suite, you would hear – assuming all tests passed – “n tests run with no failures. You rock!”
It’s a silly example, but it does make the point that you can do interesting – and unexpected – things with your test results.
Other ideas for customer reporters:
We’ve really only scratched the surface with what you can do with custom reporters for Minitest. Remember, it’s just Ruby, get in and play. At this point in the series, you should be able to implement any customization you want, be it new assertions or expectations, progress reporters, or customized reporters.
I hope you’ve enjoyed reading this series as much as I’ve enjoyed writing it. I’ve learned a lot from getting into Minitest’s code and trying to understand it , and I would encourage you to do the same; it’s small, it’s very approachable, and it’s pretty funny. Just start with autorun.rb and follow the code.