RSpec: Have xit Complain When Example Is Fixed

RSpec helps us to test our code. As you might already know, examples may be declared pending (most probably because a feature can be described even if not yet implemented). As a different use case, when you intentionally break things, you can use xit to temporarily declare an existing example pending. And, RSpec can notify you when an example you've declared pending gets (un-)intentionally fixed. This can be accomplished by wrapping misbehaving code into a pending block. Unfortunately, you cannot have both features together - the one-character pending declaration and the FIXED notification. But you can at least try. The following monkey patch replaces RSpec's xit with an incarnation that tells you when pending examples pass. It does not look quite as simple as you could have expected: class << RSpec::Core::ExampleGroup # replaces original xit with a pending fixed complaining one def xit(desc=nil, *args, &block) it(desc, *args) do example.metadata[:caller].delete_if {|line| line.include?("in `xit'")} pending("not yet green test") {self.instance_exec(&block)} end end end Things you might not have expected: self.instance_exec is required for matchers to be available to your example. Dive into RSpec's code to get to know why ... The example.metadata[:caller] is modified for the purpose of displaying the correct location of your example code. Without this modification, RSpec would display the line containing it within the definition of xit above. For unknown reasons, you'll nevertheless get that line sometimes when the example is displayed as pending. Not always, just sometimes, even though deterministic. Additionally, there is even a self-test to notify you when this monkey patch is broken (or, more probably, has been removed by someone who considered it superfluous). It does not quite accomplish a good code / test code ratio yet: module RSpec; module Core describe Example, "xit" do let(:example_group) { ExampleGroup.describe("example group") } def example_failed(example) @failed_examples << example.description end let(:reporter) { Reporter.new.tap {|r| r.register_listener self, :example_failed } } before do @failed_examples = [] end describe "with fixed example" do before do example_group.xit "fixed it" do nil.should be_nil end end it "should fail" do example_group.run reporter @failed_examples.should include('fixed it') end end describe "with not fixed example" do before do example_group.xit "not fixed yet" do nil.should_not be_nil end end it "should not fail" do example_group.run reporter @failed_examples.should_not include("not fixed yet") end end end end; end Have fun with RSpec!

More great blog posts from Kai-Uwe