David Chelimsky

random thoughtlessness

RSpec plays nice with others

RSpec 0.9 is coming soon, and promises to do a better job of playing nice with others.

RSpec plays nice with you.

There’s a new configuration mechanism inspired by the one in Rails:

<code>Spec::Runner.configure do |config|
config.use_transactional_fixtures = true
#etc
end
</code>

This mostly affects Spec::Rails users, but as new features become configurable, this will be the “how”.

RSpec plays nice with mocha.

Update 4/25 – 0.9 will be released with support for flexmock as well as mocha.

Speaking of new configurable features, RSpec 0.9 will play nice with other mock frameworks. The first release is going to support using mocha, but we’re hoping to follow this with support for flexmock as well. So now, in a spec_helper.rb, you can do this:

<code>Spec::Runner.configure do |config|
config.mock_with :mocha
end
</code>

This tells RSpec to load up mocha instead of RSpec’s mock framework, Spec::Mocks. One nice thing about using mocha with RSpec is that, in my opinion, the different syntax illuminates the different semantics:

  • expects for pre-action, interaction expectations

  • should for post-action, (typically) state expectations

Sure, we could get similar syntax by aliasing #should_receive, but there are a lot of people who simply prefer mocha or flexmock and we want to invite them all to use RSpec without giving up their preferred mocking framework.

RSpec even plays nice with ‘test/unit’

For some time now, RSpec has allowed you to bring the myriad plugins and add-ons associated with ‘test/unit’ by deriving the binding in which examples are run from Test::Unit::TestCase (like so…)

<code>describe Thing do
inherit Test::Unit::TestCase
#...
end
</code>

With RSpec 0.9, we’ve added the reverse: Spec::Expectations and Spec::Matchers made available to your ‘test/unit’ tests with a simple require statement:

<code>require 'test/unit'
require 'spec/test_case_adapter'

class ThingTest < Test::Unit::TestCase
def setup
  @thing = Thing.new
end

def test_thing_should_be_friendly
  @thing.should be_friendly
end
end
</code>

RSpec plays nice with your diet

With RSpec 0.9, RSpec is finally sugar-free!

We’ll miss the underscores that followed #should, and I’m personally grateful to Rich Kilmer for the very, very cool patch that not only allowed us to eliminate all of those nasty dots, but taught me one of my first serious lessons in meta-programming. However, as time went on we discovered that continuing to support that sugar was going to be an ongoing struggle between RSpec and any system it was being used to describe that happened to like #method_missing as much as RSpec did. And so we bid it a bittersweet “adieu”.

Of course, if you’re missing some sweetener, there’s always the natural sweeteners in mocha ….

Describe it with RSpec-0.9. Coming soon to a gems directory near you.

Describe it with RSpec

module BddTools

describe RSpec do
  it "should help you get the words right" do
    Kernel.should respond_to(:describe)
    Behaviour.should respond_to(:it)
  end
end
end</code>

That code produces this output:

<code>BddTools::RSpec
- should help you get the words right
</code>

Behaviour Driven Development is all about getting the words right.

Dan North has just registered rbehave at rubyforge and is using rspec to drive ITS behaviour. Getting started, he felt that “context” and “specify” weren’t speaking to him, so he wrapped them in “describe” and “it” to create the syntax in the example above.

These new words have been added to rspec’s trunk and will be released with rspec-0.8.3. Combined with their elder siblings “context” and “specify”, you’ll now be able to choose from a wider set of words to get your specs to “speak”.

Keep your eyes peeled for rbehave, an Acceptance Testing Framework that provides a Story Runner designed to promote communication between Customers, Developers and Testers. Combine rbehave’s Story Runner with rspec describing lower level behaviours and you’ll have the beginnings of the perfect toolset for a BDD project in Ruby.

lesson learned - don’t fork a moving target

In rspec-0.8.0, I forked assert_select in order to get the desired rspec syntax and error messages. Yesterday, only a day after the 0.8.1 release, the specs for “response.should have_rjs” (rspec’s assert_select_rjs port) started failing against edge rails.

It didn’t take me very long to realize that forking assert_select (and its siblings – assert_select_rjs, assert_select_email, etc) was a big mistake. The only thing that is really maintainable is a very thin wrapper. As long as the API doesn’t change, the wrapper should continue to work correctly.

So I re-wrote have_tag, with_tag (for nested tags), have_rjs, be_feed and send_email to simply delegate off to assert_select – exactly as i.should have_done in the first place. This rewrite will be released with 0.8.2, probably later today.

Unfortunately, this does introduce a regression. Currently, assert_select_rjs does not support :hide or :effect, so these won’t be supported by should have_rjs.

In the short term, you’ll still be able to spec :hide and :effect using the now deprecated should_have_rjs, and hopefully we’ll be able to come up w/ some solution for should have_rjs(:hide/:effect) before 0.9, when we are slated to remove all the deprecated features.

Spec::Expectations and Test::Unit::TestCase, together again at last

In Rob Sanheim’s blog comparing test/spec w/ rspec, Rob pointed out that he had “been following RSpec, the better known Ruby BDD library for awhile, but decided against it since it just doesn’t look practical for use in an established project with around ~400 test cases.”

As it turns out, rspec-0.8 has done a much better job of isolating components. It’s not quite ideal yet, but it is sufficient to support using RSpec’s expectations right in your Test::Unit::TestCases.

matchers doing double duty

When we added generated spec descriptions to RSpec, we got a surprise second use for Matchers nearly for FREE!

With only a slight modification to Spec::Mocks, we are able to use the Matchers as Mock Argument Constraints as well. For example, #equal(obj) can now be used as an Expectation Matcher:

<code>x = 5
x.should equal(3)

=> expected 3, got 5 (using .equal?)</code>

or a Mock Argument Constraint Matcher

<code>thing = mock("thing")
thing.should_receive(:msg).with(equal(3))
thing.msg(5)

=> Mock 'thing' expected :msg with (equal 3) but received it with (5)
</code>

Thanks to Dan North for pointing me to Hamcrest when we were first discussing the new expectation mechanism. It turns out that Hamcrest and jMock already employ a similar idea in Java, in which a common set of Matchers is utilized by both projects.

This is a brand new addition, and is not yet fully baked. As of now (rev 1533), there are no special methods added for better mock syntax, but we’ll probably add things like #greater_than(n) so you can say #with(greater_than(n)) instead of #with(be > n), although that WILL work, however strange the syntax.

Trunksters: this is available as of rev 1533.

Everyone else: this will be released with rspec-0.8.0

generated spec names keep specs DRYer

[Updated on 2/25]

Have you seen this show up in your specs?

specify "should be empty" do
  @group.should be_empty
end

As of rev 1519, RSpec will now take (almost) all of the stock expectations and auto-generate spec names for you. So this:

context "A Group" do
  ...
  specify do
    @group.should be_empty
  end
end

A Group – should be empty This works for all of the standard expectations except those that take blocks, which would result in names like “should satisfy block”, which doesn’t seem that helpful.

Custom Expectation Matchers Getting this to work w/ your custom matchers is a snap. Just implement #to_s in the matcher to return a String with everything after “should ” or “should not ”. For example, Equal#to_s in RSpec looks like this:

def description
  "equal #{@expected.inspect}"
end

So the following specs:

specify do
  result.should equal(3)
end

specify do
  result.should_not equal("this string")
end

would result in the following output:

- should equal(3)
- should not equal(\"this string\")

custom expectation matchers

RSpec 0.8 introduces the concept of “Expectation Matchers”:/articles/2007/02/18/expectation-matchers to RSpec. Not only does this simplify RSpec’s own internals, but it also makes it really simple to write your own custom expectation matchers.

Here’s an example that came from a question on the rspec-devel mailing list.

Evgeny wanted a simple, DRY way to specify that a Rails model class should require specific fields. Here’s what I came up with. I’m not convinced this is the best approach to this problem, but I’m presenting it here to demonstrate the simplicity of creating a custom matcher.

<code>module ModelSpecHelper
class Require
  def initialize(attr)
    @attr = attr
  end

  def matches?(model)
    @model = model
    model.send("#{@attr.to_s}=".to_sym, nil)
    return !model.valid?
  end

  def failure_message
    "expected #{@model.inspect} to require #{@attr.inspect}"
  end
end

def require(attr)
  Require.new(attr)
end
end

context "User behaviour" do
include ModelSpecHelper

setup do
  @user = User.new(:email => 'a@b.com', :zip => '02134')
end

specify "should require email" do
  @user.should require(:email)
end

specify "should require zip" do
  @user.should require(:zip)
end
end
</code>

The only thing I’m not comfortable with is that these specs are bound together with the setup. If you don’t initialize the required attributes in setup and only one of the required attributes is actually implemented in the model, all of them will pass anyway (false positive).

I guess that could be solved by adding a parameter to #require:

<code>def require(attr, valid_value)
Require.new(attr, valid_value)
end
</code>

Then #matches? could assign nil to the attr and expect #valid? to return false and then assign valid_value to the attr and expect #valid? to return true. The failure message could say something like “model was not valid even when email was assigned ‘a@b.com’. You must ensure that all required attributes are assigned values before calling #should require.”

Making that change would be cake! Again, the point here is to show how easy custom matchers are to implement. Coming up w/ the right syntax and messaging is another problem, but RSpec does its best to stay out of your way as you explore that problem.

So I encourage you to explore custom matchers if you’re using RSpec >= 0.8, and I look forward to hearing about your experiences doing so. I also encourage you to consider publishing your own libraries of custom matchers that you find generally useful.

expectation matchers

Expectation Matchers

RSpec 0.8 introduces a new approach to setting and verifying expectations called Expectation Matchers.

rspec-exts

Yurii Rashkovskii has just released 0.1 of RSpec Extensions.

This is a really exciting thing to see as there are a LOT of good ideas about extending RSpec that we’ve been cautious about adding in an effort to keep things simple, focussed and general. Hats off to Yurii for taking it upon himself to get his ideas out to the rest of the community.

My hope is that, as rspec matures, more extension libraries like this will appear and that, as specific bits of functionality prove their usefulness in a general way, we’ll be able to incorporate them into rspec down the road.