RSpec formatters

RSpec has a bunch of formatters you can use when running your tests. The default is the “progress” formatter, which prints out a green dot for every spec tat passes and a red “F” for every spec that fails. There are more, like the “documentation” and “html” formatters.

This week, the challenge is to create your own formatter for RSpec 2. Your solution should solve a problem you’re facing with the existing formatters (like, I don’t know how long my specs are going to take or I don’t notice when my suite is done running) or you can do something completely crazy and funny. With rainbows, or something like that. Oh, and remember: You’re not limited to terminal output, do whatever you can think of.

Creating an RSpec formatter is quite straightforward. Be sure to check out the existing formatters, since you’re probably going to extend one of those. For more information, you can check out the Fuubar source.

When you’re done, put your solution in a Gist, including a README.(markdown|textile) file to explain what it does, how it works and why it should win. Of course, you’re encouraged to put a link to a demo video of your formatter in action in your Gist too.

You have a week to enter, so that should be enough to think of something great. Good luck!

This contest is finished

Congratulations to this week's winners! The entries and the contestant names are shown below.

  • README.markdown

    SpecCoverage formatter

    This formatter loads SimpleCov a code coverage reporting tool for Ruby 1.9. SimpleCov already does a good job in making it really easy to configure and load itself, but even good efforts can be improved upon.

    The only problem is that you'll need to have something like a spec_helper to load it. You might not have this or you might find it ugly having to resort to environment variables to turn it off or on.

    With this formatter, SimpleCov will start, and it will load a .coverage file in which you can add ruby code to configure SimpleCov in a non-obtrusive way. Configuration for a typical Rails app will look like this:

    SimpleCov.start 'rails'
    

    To run this formatter, place it somewhere in your load path (like inside your spec directory).

    This formatter doesn't have any output, so you'll probably want to add another formatter. I prefer the documentation (if the number of specs is limited) or Fuubar formatter (for big spec suites):

    rspec spec -f SpecCoverage -f Fuubar
    

    If you are using bundler, then don't forget to add simplecov to your Gemfile:

    gem 'simplecov', :group => :test, :require => false
    

    More information on SimpleCov can be found here.

    spec_coverage.rb
    require 'rspec/core/formatters/base_formatter'
    require 'simplecov'
    
    # This formatter does nothing else but run SimpleCov. That means that if you run this formatter on
    # its own, you won't get any output. It is advised to add your favorite formatter, like this, to see
    # test failures and so on:
    #
    #   rspec spec -f SpecCoverage -fd
    #
    class SpecCoverage < ::RSpec::Core::Formatters::BaseFormatter
    
      def initialize(*)
        super
        add_default_filter
        load_simplecov_config
        start_simplecov
      end
    
      private
    
      # This is an RSpec filter, so we can safely assume that specs should be ignored
      def add_default_filter
        SimpleCov.add_filter '/spec/'
      end
    
      # Load a local .coverage file, to customize it yourself
      #
      # Example contents of this file:
      #
      #   SimpleCov.start do
      #     add_filter '/foo/'
      #   end
      #
      # Rails users might want to add at least something like:
      #
      #   SimpleCov.start 'rails'
      #
      def load_simplecov_config
        load config_file if config_exists?
      end
    
      def config_exists?
        File.exist?(config_file)
      end
    
      def config_file
        File.expand_path(".coverage", SimpleCov.root)
      end
    
      # If you didn't start SimpleCov in your .coverage file, start it now
      def start_simplecov
        SimpleCov.start unless SimpleCov.running
      end
    
    end
    
    View full entry
    Finished in 1st place with a final score of 3.2/5. (View the Gist)
  • FortuneFormatter.rb
    require 'rspec/core/formatters/progress_formatter'
    require "net/http"
    
    class FortuneFormatter < RSpec::Core::Formatters::ProgressFormatter
      def stop
        super
        print_fortune if all_passed?
      end
    
      def all_passed?
        failure_count == 0 && pending_count == 0
      end
    
      def print_fortune
        response = Net::HTTP.start("brenocon.com") { |connect| connect.get "/fortune.cgi" }
        message "\n<====FORTUNE=============>"
        message response.body.scan(/<PRE>.(.*).?<\/PRE>/im).flatten.first.chomp
        message "<========================>"
      end
    end
    
    README.textile

    FortuneFormatter

    Get your fortune now! Let all your specs pass and the next fortune will appear.

    Usage

    $ rspec -r ./fortune_formatter.rb -f FortuneFormatter

    Notes

    • Doesn´t work very well with webmock :-)
    View full entry
    Finished in 2nd place with a final score of 2.9/5. (View the Gist)
  • README.md

    YELLY!!!!! THE YELLINGIST FORMATTER EVER!!!!!

    Yelly likes to yell. To make the magic happen for simple cases just invoke him ala:

    bundle exec rspec --require=yelly_the_yellingist_formatter.rb --format=YellyTheYellingistFormatter spec/models/your_file_here_spec.rb
    

    In closing, >:O

    yelly_the_yellingist_formatter.rb
    require 'rspec/core/formatters/documentation_formatter'
    
    class YellyTheYellingistFormatter < RSpec::Core::Formatters::DocumentationFormatter
    
      def example_passed(example)
        dabble_in_that_which_should_not_be_dabbled('yes!', example)
        super(example)
        shout!('yes!', example)
      end
    
      def example_failed(example)
        dabble_in_that_which_should_not_be_dabbled('no!', example)
        super(example)
        shout!('no!', example)
      end
    
      def example_pending(example)
        dabble_in_that_which_should_not_be_dabbled('meh', example)
        super(example)
        shout!('meh', example)
      end
    
      private
    
      def dabble_in_that_which_should_not_be_dabbled(say_what_now, example)
        example.metadata[:description] = "#{say_what_now.upcase} - #{example.metadata[:description]}"
      end
    
      def shout!(say_what_now, example)
        return unless you_got_the_pipes?
        system("say", say_what_now)
      end
    
      def you_got_the_pipes?
        @pipes_present ||= system('which say > /dev/null')
      end
    
    end
    
    View full entry
    Finished in 3rd place with a final score of 2.7/5. (View the Gist)
  • README.markdown

    SARMUMY

    Tihs gem pdveiors a cutsom fmteaortr for ReSpc taht jmbleus yuor test ouuptt.

    This is, of csroue, a vrey silly tinhg to want to do with yuor tset optuut.

    UAGSE

    $ rspec -f JumbledFormatter spec
    

    HOW DEOS IT WORK?

    It scmberals the lrteets of wrods in yuor tset otuupt, but it does not cnaghe the frsit and last ltaetr of thsoe words. It is raelly ssprurinig to see how lbiegle jlmeubd txet lkie this can raelly be!

    With repsect to ReSpc, this cusotm ftoatremr is a ssblcaus of the DocumentationFormatter.

    WHY?

    Bsaceue it is slliy and sligipnsrury lgelbie (for sorht words, at any rate).

    jumbled_formatter.rb
    require 'rspec/core/formatters/documentation_formatter'
    
    # Add icsnnate mtheod +jbmule+ to Sntrig.
    class String
    
      # Jeblmus up the lttrees of a wrod but lveeas frsit and last chars in pclae.
      def self.jumble_term(s)
        return s if s.size < 4
        chars = s.split(//)
        [chars[0], *chars[1..-2].shuffle, chars[-1]].join('')
      end
    
      def jumble
        self.gsub(/[a-zA-Z]+/) do |match|
          self.class.jumble_term(match)
        end
      end
    
    end
    
    class JumbledFormatter < RSpec::Core::Formatters::DocumentationFormatter
    
      # Aoivd mgninug the ANSI cloor epecsas.
      # A bit of a klugde but it wrkos.
      # Has the sdie-ecfeft of lvaenig the guorp nemas anole.
      def color(text, color_code)
        super(text.to_s.jumble, color_code)
      end
    
    end
    
    View full entry
    Finished in 4th place with a final score of 2.5/5. (View the Gist)
  • _README.md

    Two formatters i made, just for fun, both heavily based of the specdoc format.

    The dragon_formatter.rb just says "HERE BE DRAGONS" after pending tests, and tells you the count in the summary.

    the boss_formatter.rb just adds "LIKE A BOSS!" after passing tests, and when it says how long it took.

    dragon_formattter.rb
    require 'rspec/core/formatters/documentation_formatter'
    
    # fun formatter for rspec
    class DragonFormatter < RSpec::Core::Formatters::DocumentationFormatter
    
      def pending_output(example, message)
        yellow("#{current_indentation}#{example.description} (HERE BE DRAGONS: #{message})")
      end
    
      def summary_line(example_count, failure_count, pending_count)
         summary = pluralize(example_count, "example")
         summary << ", " << pluralize(failure_count, "failure")
         summary << ", " << pluralize(pending_count, "DRAGONS") if pending_count > 0
         summary
       end
    
    end
    
    boss_formatter.rb
    require 'rspec/core/formatters/documentation_formatter'
    
    # Assertive formatter for rspec
    class BossFormatter < RSpec::Core::Formatters::DocumentationFormatter
    
      def passed_output(example)
        green("#{current_indentation}#{example.description} LIKE A BOSS!")
      end
    
      def dump_summary(duration, example_count, failure_count, pending_count)
        super(duration, example_count, failure_count, pending_count)
        # Don't print out profiled info if there are failures, it just clutters the output
        dump_profile if profile_examples? && failure_count == 0
        output.puts "\nFinished in #{format_seconds(duration)} seconds LIKE A BOSS!\n"
        output.puts colorise_summary(summary_line(example_count, failure_count, pending_count))
      end
    
    end
    
    View full entry
    Finished in 5th place with a final score of 2.0/5. (View the Gist)