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 thisMore 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.
Right. I'm actually very much in favor of using a one-liner when the doc string you would write is identical to the one RSpec can generate for you in the one-liner. IMO, this is what the one-liner feature was intended for and is a great use for it.
However, I personally get a lot of value out of longer, more intention-revealing doc strings. For example, here are some from the code base I work on at my job:
These longer doc strings help explain the why for a particular behavior, and help guide future decisions when we consider changing a behavior. (We change it, watch a spec break, and read the doc string to remind us the reason why it was there in the first place.)
Hard to answer that kind of thing without seeing the full example.
IMO they are worse. Long time users to RSpec understand that
selfindescribeis different fromselfinit-- but new user don't generally know that. The examples you showed there all define an alternateexpectand/or an alternateitthat does something else from what they normally do. It could work because you've switched the example vs. group context (e.g. you're usingexpectin the group anditin the example), but I think it'll cause confusion. On top of that, it requires changes both to the example group API (e.g. to add a method likeexpectorexpect_it) and to the example API (e.g. to addtoorit). I like the simplicity ofis_expected: it is exactly equal toexpect(subject)and is very easy to understand. It doesn't require new methods in two contexts. It doesn't use an existing method name for something new.FWIW, I originally proposed something very similar when I first proposed adding the
expectsyntax:rspec/rspec-expectations#119 (comment)
I'm glad we didn't go that route; I find
is_expectedto be much simpler and less confusing.That's one thing I hope!
If you want more DSL, give rspec-given a try. It layers some additional DSL constructs on top of DSL and, IMO, is a more well-thought out, consistent approach than rspec-core's one-liners or
its.Personally, I find I prefer the simplicity of starting with the simpler constructs (
describe,it) and then, as need arises, using some of the additional constructs likelet,before, etc (and, occasionally, a one-liner orits).