Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
$ rspec ~/insane-magic.spec
1) Rspec should not allow you to make shit up
Failure/Error: [1, 2, 3].should have(4).potatoes
expected 4 potatoes, got 3
# /Users/graham/insane-magic.spec:5
Finished in 0.00042 seconds
1 example, 1 failure
# See the `potatoes` method call? Where the hell did that come from?
# Is it in rspec? It must be, surely. That's all I've required!
# [Goes off to grep the rspec gems for "potatoes"... doesn't find it]
# Hmmmm. Good for people new to testing you say? Really?
# The "choose your own method name and we'll ignore it" behaviour is
# not syntactic sugar, it's poison for people who are new to programming.
# It's a bloody daft idea to put it forward as good practice in a widely
# popular library.
# To be clear, `describe` and `it` I like. But you can stop there; there's
# just negative productivity to be had from there on in. The Contest gem adds
# support for these blocks to test/unit in 100 lines of code.
require 'rspec'
describe 'Rspec' do
it 'should not allow you to make shit up' do
[1, 2, 3].should have(4).potatoes

gma commented Mar 29, 2011

Now that a newbie Ruby programmer has got over the fact that grep has failed them, could they start to wonder how some objects seem to have a should method defined on them? And have, where did that spring up from? They're not mentioned in their Ruby book. Perhaps they conclude that their Ruby book covers 1.8.7 and they're using 1.9; perhaps should is a new method on Array in Ruby 1.9? Yeah that's probably it...

This kind of thought process goes on all the time when somebody is learning a new language. This "DSL" (there's another frigging annoying fashion based Ruby meme) drives my sympathetic side nuts.

jgwhite commented Mar 29, 2011

Brings this great post to mind:

Exploration and play at the meta level are really enticing aspects of Ruby. They make it a great language for beginners. On the flipside, it is very easy to get addicted to this sort of sugar and wind up never really implementing anything besides seemed-clever-at-the-time syntax.

rgarner commented Oct 26, 2011

Grep failure on new dev confusion applies to every library that uses method_missing. ActiveRecord is right up there. During my Ruby learning curve, this confusion you state applied (and still applies, until you learn the idioms/fashions (delete according to mood)) to way more than RSpec.


gma commented Oct 26, 2011

Well quite – method_missing should be reserved for special occasions.

The ActiveRecord API that springs to my mind does something useful that you can't already do with Ruby's standard library.

The difference with RSpec is that everything it does can be done with the Ruby standard library.

rgarner commented Oct 26, 2011

I suspect many more libraries are susceptible to the reductio ad absurdum attack, though :)

RSpec makes it easier to do stupid stuff, for sure. I also think that when used well, the resulting code makes far more immediate sense with less cognitive overhead at 17.30 on a Friday afternoon when you're sleepy after a nice lunchtime pie. It might be the case that it's just what I got used to, having skipped Test::Unit and coming to it straight from C#/MSpec.

[1, 2, 3].should have(3).numbers makes sense to me - it requires zero parsing beyond what I already do for English, whereas assert_equal(3, [1,2,3].length) always leaves me having to expend at least a small amount of energy to get to a mental model of "[1,2,3] should have 3 numbers". I'd prefer not to have the parsing overhead, especially if I'm taking in a dozen failures at a time.

Of course, this is all rationalisation from me having a blind spot remembering the sequence of actual, expected in an assert_equal and finding remembering that have(n) returns a matcher and should is mixed into everything is much easier than forcing myself to internalise a different and unnatural order for subject -> predicate -> object. Just wired that way, I suppose.

But I also like stuff like

    subject { }
    its(:name) { should be_nil }
    its(:count) { should eql(0) }

and too often I see an equivalent Test::Unit suite of

    def test_attributes_should_initialize
        object =
        assert_equal 0, count

In a CI environment, when that test fails, do you know which assertion failed? Do you care? Does it help to know? Maybe not in this contrived example, but how about if there were a dozen asserts? Does Test::Unit have an equivalent of the RSpec version where the code under test is called once and multiple identifiable assertions are made (is it Contest)? If so, why doesn't it get used more? If it exists, I'll try Test::Unit on my next project and report back.

Wow. Sorry that was so long. I probably could just have said "it's a matter of taste" ;)

EDIT: please allow me to admit that a lot of this response didn't have anything to with the have(n) matcher and was mostly for @techbelly

I think it's more than just taste. I appreciate the confusion that @gma is complaining about, and would be more than happy to look at alternatives. For example, instead of a method call, we could change that API to [1,2,3].should have(2, :items). Reads just as well, but now you can look at the rdoc to understand it. The current rdoc does explain methods like potatoes, but I agree that when trying to understand a method call, the instinct would be to grep for its definition rather than look at the rdoc for a different method.

FYI: rspec/rspec-expectations#93. Feel free to add commentary there. I proposed one alternative, and would love to hear other proposals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment