Ruby Redos: Rspec

I have always found Rspec syntax fascinating. There is something incredible about being able to type readable English sentences and have them do exactly what you think they will from reading them.
I wanted to take a stab at recreating something like Rspec and it was a great exercise. Test::Unit has always felt foreign and this little experiment was a great reminder of why: It ignores Ruby’s blocks.

Test::Unit works by gathering all the decendents of Test::Unit::TestCase and calling every method that starts with “test_”:

class TestMyClass < Test::Unit::TestCase
  def test_my_method1

  def test_my_method2

This approach, to my eyes, is a relative of the command pattern, which is essentially a workaround for the lack of language support for blocks. It does not make much sense to use the command pattern in Ruby but you still see it pop up all over the place.

Rspec syntax, on the other hand, is really a just a bunch of Ruby blocks:

[14] pry(main)> load 'test.rb'
=> true
[15] pry(main)> describe "Thing" do                                                                       
[15] pry(main)*   it "is nil" do                                                                          
[15] pry(main)*     expect(nil).to be_nil                                                                 
[15] pry(main)*   end  
[15] pry(main)* end  
A Thing:
is nil

You will notice that describe is defined on the main object and that everything else I have put in a TestContext class to house the methods that I am expecting to be called in the blocks. Then I am using instance eval to execute the block as though it were part of the TestContext class. One other thing to point out is that calling will grab any block that has been passed to the method:

require 'benchmark'

def describe subject
  puts "A #{subject}:"
  block =
  Benchmark.measure do

class TestContext

  def initialize
    @examples = {}

  def run block
    instance_eval &block
    @examples.each_pair do |desc, code|
        puts "  \033[32m#{desc}\033[0m\n"
        puts "  \033[31m#{desc}\033[0m\n"
      puts "-----"

  def it its_description
    @examples[its_description] =

  def expect thing

  def be_nil


class NilMatcher

  def match subject


class Assertion

  def initialize subject
    @subject = subject

  def to what


I like the fact that the emphasis is on how a few methods read when chained (describe, it, expect, to, be_nil) and using those as a thin layer around blocks. This is some of the core stuff that that makes Ruby great to work with and, in my opinion, a great example of what Matz is talking about when he says “Languages can be weapons against complexity”.
He has given us blocks in the language so things as common as “wanting to execute code later” is actually part of the language. No design pattern required. The flexibility of the language also means we can write code with a very small distance between what is said and what is meant, further reducing the complexity. Both of those things are on display in Rspec.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s