How to write your very own RSpec formatter and have fun doing it

* Breaking news! We’re the winner of the CodeBrawl RSpec formatters 2 contest!

The story

A few weeks ago while travelling by train back from Rupy, we spent several long hours without an Internet connection. Having nothing better to do, I opened up the rspec-core gem and started digging into how formatters work – something I had been planning to do for quite some time, but never got a chance. What I found was a very clean code with good documentation, so we decided to write our own formatter straight away. As Chuck Testa was quite popular at that time, a Chuck’s Testa Rspec formatter seemed to be a natural choice…

What we’ve come up with is now available here. Feel free to give it a try. It made us laugh a couple of times ;)
If, however, ChuckTestar does not seem like the ultimate formatter for you, and you feel like doing something yourself, here’s a short guide to get you started.

The tutorial

Let’s write a very simple formatter that will work just like ProgressFormatter, but it will additionally output a simple message after all the specs are run. We’ll start by installing the latest RSpec gem (at the time of this writing, the version is 2.7.0):
  gem install rspec
Let’s add a dummy spec – something we’ll use to observe our formatter in action:

  describe "Dummy" do
    it "does nothing" do
      1.should eql(1)
Try running the spec:
  rspec dummy_spec.rb
The output will look something like:
  Finished in 0.16405 seconds
  1 example, 0 failures
Now let’s add our formatter class:

  require 'rspec/core/formatters/base_text_formatter'
  class MyFormatter < RSpec::Core::Formatters::BaseTextFormatter

We are inheriting from ProgressFormatter as we only want to alter a small piece of its functionality. Let’s try running the specs with our formatter:
  rspec -r ./my_formatter.rb dummy_spec.rb --format MyFormatter
Obviously the output will be similar to the previous one as we haven’t changed any functionality yet. Now, let’s look for a moment at the ancestors of our formatter. The ProgressFormatter inherits from BaseTextFormatter which in turn inherits from BaseFormatter. The methods implemented in BaseFormatter are either self-explanatory or are accompanied by a short description.
Our goal is to put a random message after the summary, so let’s alter the dump_summary method a bit:

  require 'rspec/core/formatters/progress_formatter'

  class MyFormatter < RSpec::Core::Formatters::ProgressFormatter
    def dump_summary(duration, example_count, failure_count, pending_count)
      output.print ("Awesome\n")
Now when you run the spec again:
  rspec -r ./my_formatter.rb dummy_spec.rb --format MyFormatter
The output should look something like:
  Finished in 0.13848 seconds
  1 example, 0 failures
You can find this simple example on my github account.
Obviously this is just a beginning. As we are allowed to run arbitrary code to handle several events (like example_started, example_passed etc.) the possibilities are endless. Now, how can this be useful?

The ideas

One of the ideas we had was suggested by Michał and was inspired by one of the DevMeetingsPL workshops. Imagine you want to run a contest, where attendees are asked to write some Ruby code that makes a prepared specs pass. Whenever they run the specs, your formatter sends data to a common server, which then displays the current status of each attendee. This way you get a nice score board providing live status of the competition.
CodeBrawl organized an RSpec formatters contest twice (contest 1 and contest 2)*, so if you’re looking for inspiration, you should check the submissions there too.
That’s it. If you haven’t already, I hope you now know that it’s very hard to write an RSpec formatter and it’s not fun at all… Nope!
This entry was posted in Tutorials and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>