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.

must_be_kind_of AbstractReporter

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 started

- Called when the run has started record - Called for each result, passed or otherwise

- Called for each result, passed or otherwise report - Called at the end of the run

- Called at the end of the run passed? - Called to see if you detected any problems

must_include :example

For 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:

Line 1: Like all Minitest plugins, we’re adding to the Minitest module

Lines 2-4: The init method which has been discussed previously

Line 3: We append an instance of our VocalReporter to Minitest’s CompositeReporter which handles execution of the four methods ( #start , #record , #report , and #passed? )across all reporters

to Minitest’s CompositeReporter which handles execution of the four methods ( , , , and )across all reporters Line 6: Naming the VocalReporter class and inheriting from the StatisticsReporter

class and inheriting from the StatisticsReporter Lines 7-20: Our report method which will output our results

method which will output our results Line 8: calls StatisticReporter ’s #report method in order to get access to attributes set therein, such as #failures and #count

’s method in order to get access to attributes set therein, such as and Line 10: calculate the percentage of fail

Lines 12-18: a little logic to define the message

Line 19: output the message audibly

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.

@ideas.wont_be_empty

Other ideas for customer reporters:

Output results to a USB LED device using blinky

Send emails to everyone on the team announcing your success or failure (Public shaming always leads to increased motivation. Just ask anyone who’s tried dieting.)

Output statistics to a database to track progress

Send results to a Continuous Integration server

Make a game out of the results

Other ideas? List them in the comments below

must_be_kind_of Conclusion

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.