Fork me on GitHub

Pending Examples via NotImplementedError in rSpec

This post has been updated at 16/05/2013.

NotImplementedError can be sometimes really helpful. Unfortunately rSpec shows them as a failure, but aren't they just the exact equivalent of a pending message?

I realised it today, when I've been porting some parts of the AMQP gem on AMQ Client. There's a lot of raise NotImplementedError in AMQP currently and in specs, they all are displayed as failures, but it's not exactly true, it's rather a nonimplemented feature than a bug.

Fortunatelly it's easy to fix:

RSpec::Core::Example.send(:include, Module.new {
  def self.included(base)
    base.class_eval do
      alias_method :__finish__, :finish
      remove_method :finish
    end
  end

  def finish(reporter)
    if @exception.is_a?(NotImplementedError)
      from = @exception.backtrace[0]
      message = "#{@exception.message} (from #{from})"
      @pending_declared_in_example = message
      self.metadata[:pending] = true
      @exception = nil
    end

    __finish__(reporter)
  end
})

Easy, right? Now let's try it:

# encoding: utf-8

class HelloWorld
  def from_london
    "Hello World from London!"
  end

  def from_alasca
    messgae = "Sorry, no network here, here are only bears!"
    raise Errno::ECONNREFUSED.new(message)
  end

  def from_turkey
    message = "Jeez, I have to learn Turkish first!"
    raise NotImplementedError.new(message)
  end
end

describe HelloWorld do
  # This is supposed to work.
  context "#from_london" do
    it "should work" do
      subject.from_london
    end
  end

  # This is supposed to fail.
  context "#from_alasca" do
    it "should work" do
      subject.from_alasca
    end
  end

  # And this is supposed to be pending.
  context "#from_turkey" do
    it "should work" do
      subject.from_turkey
    end
  end
end

Here's how does it look like:

Backtraces

Pending messages in rSpec don't support backtraces. Of course they don't, why would they need them? But when the NotImplementedError can be raised from any place in the code, it's getting pretty useful, so let's implement them:

module RSpec::Core
  Example.send(:include, Module.new {
    def self.included(base)
      base.class_eval do
        alias_method :__finish__, :finish
        remove_method :finish
      end
    end

    attr_reader :not_implemented
    def not_implemented?
      !! @not_implemented
    end

    def finish(reporter)
      if @exception.is_a?(NotImplementedError)
        @not_implemented = @exception
        from = @exception.backtrace[0]
        message = "#{@exception.message} (from #{from})"
        self.metadata[:pending] = true
        @pending_declared_in_example = message
        @exception = nil
      end

      __finish__(reporter)
    end
  })

  Formatters::BaseTextFormatter.send(:include, Module.new {
    def self.included(base)
      base.class_eval do
        remove_method :dump_pending
        remove_method :dump_backtrace
      end
    end

    def dump_pending
      return if examples.empty?
      output.puts
      output.puts "Pending:"
      examples.each do |example|
        pending_message = example.execution_result[:pending_message]
        output.puts yellow("  #{example.full_description}")
        output.puts grey("    # #{pending_message}")
        output.puts grey("    # #{format_caller(example.location)}")
        patterns = RSpec.configuration.backtrace_clean_patterns
        if example.not_implemented? && patterns.empty?
          dump_backtrace(example, example.not_implemented.backtrace)
        end
      end
    end

    def dump_backtrace(example,
      backtrace = example.execution_result[:exception].backtrace)
      format_backtrace(backtrace, example).each do |backtrace_info|
        output.puts grey("#{long_padding}# #{backtrace_info}")
      end
    end
  })
end

I know it's quite messy, but hey, it works! Just use the --backtrace option to get the full backtrace. Of course I'd be happy to contribute it to rSpec, I already asked David Chelimsky on Twitter if he'd be interested in such feature. Well ... enjoy it guys!

blog comments powered by Disqus
About

RSS

All Posts IT rSpec Ruby exceptions TDD BDD testing

Tags

GitHub projects

Twitter @botanicus

Recent Comments