RSpec 3 syntax is more verbose.
About twice as many characters to type to stub something:
obj.stub(client: client) # old
allow(obj).to receive(:client).and_return(client) # new
allow(obj).to receive(client: client) # possible? still much longer
allow(obj, client: client) # I might wrap it in this
More characters to type for every single assertion (multiply by number of assertions desired):
obj.should == value
expect(obj).to eq value # +4 characters
obj.should_receive(:method)
expect(obj).to receive(:method) # +4 characters
expect(obj, eq value) # +2 characters and doesn't read as well
# old frowned upon "its" extension, but look how few keystrokes, and how DRY
its(:body) { should include(text) }
# new (via transpec)
describe '#body' do
subject { super().body } # jon would disapprove of this anyway?
it { is_expected.to include(text) } # why is there one _ and one . (says my don't make me think brain)
end
its(:body) { is_expected.to include(text) }
I think there are more examples too.
But you know, I don't so much mind the extra typing, it's probably having to type (
that bugs me. That character is significantly harder to type than .
and ==
.
Was the less metaprogramming, less magical solution worth it?
I have no easy solution to this by the way. Just trying to communicate something I expressed in 140 characters better.
Thanks for writing this up!
To respond to a few specific things you brought up:
This is possible with
allow(obj).to receive_messages(client: client)
. You can pass as many message/return value pairs as you want.I wouldn't say it's frowned upon. I have specs in some projects that use it. I'd say the abuse of it is frowned upon. I've actually gotten questions like "how do I test methods that take arguments?
its
doesn't take any arguments"...to which I respond, "you write an example that calls the method with arguments;its
was never intended for that". From reading questions on stackoverflow, etc, I get the idea that some users believe the point of RSpec is to write tests in as terse a syntax as possible, and if there's not a one-liner syntax to support what they are trying to test, they feel like that's a deficiency in RSpec that should be addressed. That's not the point of RSpec at all. For an example like yours, I'd probably just test it like this:You say that your
its
example is so DRY, but there is no duplication of knowledge in my version. It's not any less DRY. Your version is definitely fewer characters, but that has nothing to do with DRY.It's this way because
is_expected
is defined simply asexpect(subject)
...sois_expected.to
is the same asexpect(subject).to
. That said, since it could trip people up, I've been mulling over addingis_expected_to
,is_expected_to_not
andis_expected_not_to
just so whatever users try works. (Kinda like how we supportexpect().to_not
andexpect().not_to
). Thoughts?I think so. Have you read my blog post about the new syntax and the reasons for its introduction? I myself experienced odd failures with proxy objects with the
should
syntax, and it took me like an hour to figure out, even though I was a contributor to RSpec!The new syntax is also much more consistent (and hopefully, easier to learn) than the old syntax. Consider that RSpec supports 3 kinds of expectations:
change
,raise_error
,throw_symbol
and yield matchers -- these are things that aren't static values and require blocks to test).Before introducing the new syntax, these three things had 3 different syntaxes:
These have now been nicely unified:
IMO, this consistency is a big win. I also think that this "wrapping" syntax makes it more explicit what RSpec's doing, which is a nice side effect.
But you're right -- these things are more verbose. Tradeoffs, as with all software engineering. I dislike excessive typing as much as anyone, but it's not RSpec's primary design goal to support tests being as terse as possible.
FWIW, we have no current plans to ever drop the old syntax (although, we may move it into an external gem in 4.0 -- that's not decided though), so if you prefer the old syntax and understand the tradeoffs, then keep using the old syntax. That's fine. Transpec even supports options to keep specific parts of the old syntax depending on what you want.
Anyhow, thanks again for engaging, and trying out RSpec 3!