[TIP] running away from RSpec

Gary Bernhardt gary.bernhardt at gmail.com
Sat Feb 12 11:58:56 PST 2011


On Sat, Feb 12, 2011 at 10:34 AM, Alfredo Deza <alfredodeza at gmail.com> wrote:
> This more of an open question I guess, but why there isn't a project in
> Python that grabs the best features from RSpec
> and implements them?

As you suggest, it's impossible. Mote and PySpec are just fancy ways
to name your tests: weak implementations of one tiny corner of RSpec.
Mote uses horrible settrace magic; PySpec adds a bunch of
domain-irrelevant noise. Neither even supports arbitrary context
strings. RSpec is more terse, more expressive, removes the noise, and
is an entirely reasonable thing to build in Ruby.

That last point is important: it's not just that RSpec is possible in
Ruby; it's actually idiomatic. I think that people don't realize just
how easy it is to build its syntax. RSpec lets you say things like:

  describe Integer do
    it "adds numbers" do
      (1 + 1).should == 2
    end
  end

Looks complicated and magical, right? Nope. Here's an implementation
of describe and it:

  def describe(name)
    puts name
    yield
  end

  def it(name)
    puts "- #{name}"
    yield
  end

This will output the spec output that's become so familiar:

  Integer
  - adds numbers

I spent a couple days of my life trying to get Python to do this when
writing Mote and failed completely.

The `2.should == 2` syntax is also simple, but here you can get close
in Python. Expecter Gadget [1] lets you say `expect(1 + 1) == 2`. I
still prefer RSpec because the statement reads as "subject,
relationship, expected value".

There's much more terseness to be had than this, though; the above is
just the most trivial example. You can say things like:

  describe Dog do
    it { should have(4).feet }
    it { should be_thirsty }
  end

This is totally stock RSpec (and totally self-contained).
`have(4).feet` asserts that `Dog.new.feet.size == 4`; `be_thirsty`
asserts that `Dog.new.thirsty?` returns true.

The most equivalent Python is:

    class DogTest:
        def setup(self):
            self.dog = Dog()

        def test_that_it_has_4_feet(self):
            assert len(self.dog.feet) == 4

        def test_that_it_is_thirsty(self):
            assert self.dog.is_thirsty()

(Inline the dog construction if you like; that just moves the problem around.)

Some Python programmers will probably turn their noses up at that
RSpec example because it's fancy (I know I would've). I think that's
unreasonable. It's significantly more terse *and* more readable than
the Python: a combination we don't often get.

"More readable" does depend on learning RSpec, but that's easy because
it's so intuitive. And I bet that you knew exactly what it meant upon
reading it, even if its mapping onto the Dog class wasn't obvious.

Getting back to the point: you just can't do this stuff in Python.
I've spent a lot of time over the last couple years thinking about
this and eventually I just gave up. It's one of the reasons that
almost all of my programming is now done in Ruby.

[1] https://github.com/garybernhardt/expecter

-- 
Gary
http://blog.extracheese.org



More information about the testing-in-python mailing list