-
-
Notifications
You must be signed in to change notification settings - Fork 395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for expect(value)
syntax.
#119
Conversation
I'd love to see this in 2.9, and make Regarding excluding Good stuff! |
|
@justinko 2.9 is imminent and this is going to take some time, so probably 2.10 or 3.0 (if there isn't a 2.10). If it's 3.0, I think we're safer leaving it an opt-in and make it opt-out in 4.0). |
@myronmarston what about the |
describe ExpecationTarget do | ||
context 'when constructed via #expect' do | ||
it 'constructs a new instance targetting the given argument' do | ||
expect(7).target.should eq(7) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is funny - we're using the old syntax to specify the new. Reminds me of rspec's early days when rspec was tested w/ test/unit.
Do you think expect(expect(7).target).to eq(7)
would be too much of a leap at this point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For all these examples, I started with just the new syntax and empty to
and not_to
methods, but everything (wrongly) passed since to
and not_to
were no-ops. Using should
here got me correctly passing and failing examples, and gave me confidence I was actually specifying something :).
We can certainly refactor in the future.
@dchelimsky for some reason my brain couldn't process the fact that we can increment from 2.9 to 2.10 rather than 2.9 to 3.0 😪
IMO, It feels strange to me to have a config option for a library that is opt-in. Oh well. |
@dchelimsky maybe That way we don't bind RSpec Core to opt-in libraries. UPDATE: Or rather I should say, let opt-in libs leak into RSpec Core. Right now it's all contained in |
@justinko it would be defined in rspec-expectations via What you propose, however, might be a cleaner way to handle config settings for extensions. It would certainly clarify where the options live, and it might allow us to clean up |
@dchelimsky RSpec Expectations can be run stand-alone (e.g. w/ T/U), so that would be binding it to RSpec Core 😄 |
@dchelimsky we do a |
Ever since we started turning our nose up at the noise word "should" in example descriptions, it's kind of irked me we still use it in examples themselves. So, from that perspective alone, I like this. |
|
@justinko That said, I really like the idea of a name for the extension. |
@dchelimsky and in the case of multiple expectation/mock frameworks: config.expectation_framework(:rspec).enable_should_and_should_not = true
config.expectation_framework(:test_unit)
config.mock_framework(:rr) Just setup a mapping from symbol to module/constant. Obviously, not passing a symbol would return the first framework. Sorry to take this off track folks! |
@tpope EXACTLY. Which brings up an interesting point: maybe |
@justinko |
Great question. I didn't even consider that. I don't use it very often. You could still use This actually brings up a good point in favor of the config option being exposed by rspec-core (but added by expectations, as you've suggested): it means that rspec-core will have access to it and can thereby choose not add these methods. One reason I opened this PR now was to hopefully get this new syntax in a 2.x release (potentially with class Object
def method_missing(name, *args, &block)
if %w[ should should_not ].include?(name.to_s)
warn "should and should_not are not available by default in RSpec 3.0. To enable them, do XYZ."
end
super
end
end Redefining |
Whether we release the configuration option as 2.x or 3.0, I want the default to start off the way it is now (with The |
Actually, here's a pretty decent syntax for one-liners with describe MyModel do
expect_it { to validate_presence_of(:name) }
end I'm generally reluctant to add new methods to the example group DSL, but here it's just an alias for @dchelimsky -- you're right that it does not harm to leave the |
Random thought that occurs to me about my suggested |
I'd rather figure out a way to configure things such that config.enable_should_for_one_liners = true
config.enable_should_on_every_object = false
# or
config.enable_should :scope => :global
config.enable_should :scope => :one_liners
#etc |
I vote for having separate methods for this configuration opposed to the latter. The latter option looks like the second call to #enable_should is overriding the first. |
I'm not sure I agree, but I don't necessarily disagree. Why would you prefer to keep |
There's a difference between reducing risks associated with a method added to all objects and For me, |
If its just a question of syntax for the one liners what about
Oh, and I do like the whole idea of this change. +1 |
What about 3rd party libraries that extend RSpec? I maintain such a library, which uses "should," "should_not," and "should_receive" internally. I think that if these methods are removed in a future version, I would like to still let my library work with pre-removal and post-removal versions, instead of having to maintain two branches of my code. Can this change be implemented such that libraries continue to work? |
@samwgoldman we're talking about opt-in/out, not removal, so there shouldn't be a conflict. |
@dchelimsky Okay, but just to be absolutely certain I understand: if the application owner opts out, would an extension like this still work? https://gist.github.com/1777846 |
@samwgoldman -- two suggestions:
|
Note: there's more to do here (documentation, etc); this is just a starting point for discussion and comments.
Also, fix the spelling of the class while I'm at it.
:should, :expect or both can be chosen.
I rebased (to get things current again) and implemented @dchelimsky's suggested config API. It was actually fairly easy to make it revertible as well. I'll tackle disabling operator matchers next. |
Woah, that travis notification is pretty sweet. I'll tackle fixing it tomorrow. |
* Fix build on JRuby. Our sandboxing via forking didn't work on JRuby since fork isn't available. On JRuby we just re-enable all syntaxes at the end of each sandboxed example. * Testing this revealed that the way I was restoring a disabled syntax didn't always work. Based on the random order, sometimes spec/rspec/matchers/be_spec.rb:427 would fail with "TypeError: bind argument must be an instance of Kernel". * Refactored main logic into new syntax module, that can add the syntaxes to any class or module. Kernel/RSpec::Matcher defaults are provided for convenience. This also fixes the bind failure, by redefining the methods anew rather than re-binding the old ones.
- Remove bang from method...as @justinko rightly pointed out, there's no corresponding bangless method so it didn't really follow convention here. - Use an early guarded return.
@justinko -- I just removed the bang. Thinking about it some more, I like the convention that blog post mentions. I've never thought very consciously about my bang usage. BTW, as the code stands now, when you do this: RSpec.configure do |rspec|
rspec.expect_with :rspec do |c|
c.syntax = :should
end
end ...then this will be disallowed: it 'tries to use the expect block syntax' do
expect { something }.to raise_error
end Prior to this pull request, Another thing... Finally, @justinko suggested @dchelimsky, @justinko, or anyone else: thoughts on any of these? |
Agreed on all points from me. |
[ci skip]
This is needed because we've decided not to support operator matchers off of `expect(value).to`, and `match_array` is the best name we've come up with for it.
@dchelimsky -- anything else you want changed before I merge this in? |
@myronmarston nope - have at it. |
Add support for `expect(value)` syntax.
Changes Unknown when pulling f00de57 on expect_syntax into * on master*. |
Lol coveralls bot. Although it has drawn my attention to the fact the branch still exists, can it be safely deleted now @myronmarston? |
Note: I'm opening this pull request just to start a discussion about this. It's not ready to be merged yet.
Feature Summary
This adds an alternate syntax to the existing
should
syntax for setting expectations. It's based on the already existing use ofexpect
for block expectations but makes it work for normal ones, too:I've already had a few conversions with @dchelimsky and @justinko about this. There are some details we need to work out and I also want to get feedback from a wider base of RSpec users.
Here's a summary of why I think this new syntax has value:
should
for normal expectations, andexpect
for block expectations (or you can fall back to using a lambda/proc). This unifies them: you can use expect for both.should
andshould_not
.should
andshould_not
are prone to problems related to the fact that any object can undefine or redefine them on itself and it suddenly can break rspec-expectations. We've actually seen a few cases of this (where it was completely unintentional, in fact!). Consider the case of a proxy object that usesBasicObject
, defines a method (proxy_method
) and proxies the rest to a target object usingmethod_missing
. An expectation likeproxy.should respond_to(:proxy_method)
can wrongly fail, becauseshould
will be proxied through to the target object, so this winds up beingtarget.should respond_to(:proxy_method)
. In contrast,expect(proxy).to respond_to(:proxy_method)
would work just fine. For some more cases of problems like these, see Weird expectation failures for delegated object #114, be_* predicate gives wrong results when requiring delegate before rspec rspec-core#471 and #should method on CollectionProxy rspec-rails#445.The last one is the biggie for me. I've been bitten by weird, hard-to-track-down bugs with
should
on delegate objects.Open Questions
So, open questions for discussion:
should
andshould_not
being monkey-patched ontoKernel
? The value I see here is that if people decide thatexpect
is the preferred syntax for a given project, it would be nice to be able to help enforce uniformity by preventingshould
andshould_not
from being used (which, in turn, would ensure the project never has any of the weird proxy-objectshould
issues we have seen). Note that I wouldn't consider every removingshould
andshould_not
from rspec-expectations entirely. There's too much code out in the wild that uses and it generally works fine.should
/should_not
, what should that mechanism be?expect
is the preferred syntax, would it make sense to disableshould
andshould_not
by default in some future major release (i.e. make it opt-in for that syntax, rather than opt-out).My Two Cents
I like this syntax a lot (obviously; that's why I opened this PR!), and I'd like a way to be able to disable
should
andshould_not
on future projects. I'm not yet sure what that way should be, but one possibility is the existingexpect_with
option in rspec-core. Maybe it could supportexpect_with :rspec
for all of rspec-expectations, andexpect_with :rspec_only_expect
(or something better named) for rspec-expectations w/oshould
. As for #4: if rspec users like theexpect
syntax, and we can ensure a smooth transition, and provide could deprecation messages, I'd probably be in favor of disablingshould
by default in 3.0 or 4.0, simply because it avoids a whole class of issues.If you're an RSpec user and you have any opinion on this whatsoever, please leave a comment!