Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Message expect/allow API independent of configurable syntax. #311

Closed
wants to merge 2 commits into from
Closed

Message expect/allow API independent of configurable syntax. #311

wants to merge 2 commits into from

Conversation

pda
Copy link
Contributor

@pda pda commented Jun 21, 2013

An API independent of configurable syntax for setting message expectations/allowances is necessary, as highlighted by rspec/rspec-rails#764 and #306.

Proposed interface by @myronmarston in rspec/rspec-rails#764:

RSpec::Mocks.expect_message(object, :message).with("foo").and_return(5)
RSpec::Mocks.allow_message(object, :message).with("foo").and_return(5)

I'll upgrade this to a pull request once I've written code at Rails Camp this weekend.

@coveralls
Copy link

Coverage Status

Coverage increased (+0%) when pulling 0e7c0e2 on pda:syntax_agnostic_message_matchers into 2724883 on rspec:master.

@pda
Copy link
Contributor Author

pda commented Jun 21, 2013

This proposed API is problematic:

RSpec::Mocks.allow_message(object, :message).with("foo").and_return(5)

… because the .with("foo").and_return(5) need to be called on the MessageExpectation before it's passed to the AllowanceTarget. So they can't be trailing method calls like this. It's also not clear how best to pass a block/proc/lambda implementation for the stub.

A possible solution is this:

# Simple case.
RSpec::Mocks.allow_message(object, :message)

# Using MessageExpectation methods like #with #and_return etc.
RSpec::Mocks.allow_message(object, :message) do |expectation|
  expectation.with(:in).and_return(:out)
end

# Providing an implementation for the allowed/stubbed method.
RSpec::Mocks.allow_message(object, :message, ->{ :out })

# Providing implementation AND using MessageExpectation API.
# Not sure this is a real use-case…?
RSpec::Mocks.allow_message(object, :message, ->{ :out }) do |expectation|
  # do something with expectation?
end

I'm not sure I like this, and it's 2:22am so I'm going to bed.
Feedback extremely welcome, and I'll have another go at it tomorrow.

@myronmarston
Copy link
Member

Thanks for taking a stab at this. I think you're right that if you just delegate to the receive matcher, it's problematic to provide the API I suggested. However, the receive matcher is just a wrapper over a more fundamental internal API (which is what stub and should_receive use) and I think it would make sense to use that underlying API here. After all, the receive matcher is really just there to support expect().to receive expressions. I think something like this would work:

def allow_message(subject, message, &block)
  proxy = ::RSpec::Mocks.proxy_for(subject)
  proxy.add_stub(caller(1)[0], message.to_sym, &block)
end

def expect_message(subject, message, &block)
  proxy = ::RSpec::Mocks.proxy_for(subject)
  proxy.add_message_expectation(caller(1)[0], message.to_sym, &block)
end

Note that I haven't actually tested this. (And the caller(1)[0] part I'm particularly unsure of; you'd want to verify that's the right caller line to pass along). Basically, this mirrors what stub and should_receive do.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0%) when pulling 3b2f020 on pda:syntax_agnostic_message_matchers into 2724883 on rspec:master.

@pda
Copy link
Contributor Author

pda commented Jun 22, 2013

Ah thanks, that's much better. I was looking too hard at the :expect backend, and not hard enough at the :should backend.
Your code worked pretty much verbatim for .accept_message.
I'll squash this branch into a tidy history once I've done .expect_message.

@coveralls
Copy link

Coverage Status

Coverage increased (+0%) when pulling 9f9ff96 on pda:syntax_agnostic_message_matchers into 2724883 on rspec:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0%) when pulling 7f67eae on pda:syntax_agnostic_message_matchers into 2724883 on rspec:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0%) when pulling 8024132 on pda:syntax_agnostic_message_matchers into 2724883 on rspec:master.

@pda
Copy link
Contributor Author

pda commented Jun 22, 2013

And the caller(1)[0] part I'm particularly unsure of; you'd want to verify that's the right caller line to pass along

caller(1)[0] looks right to me:

caller(1):
0: rspec-mocks/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb:30:in `block (2 levels) in <module:Mocks>'
1: gems/rspec-core-2.14.0.rc1/lib/rspec/core/example.rb:114:in `instance_eval'
2: gems/rspec-core-2.14.0.rc1/lib/rspec/core/example.rb:114:in `block in run'
3: gems/rspec-core-2.14.0.rc1/lib/rspec/core/example.rb:254:in `with_around_each_hooks'
4: gems/rspec-core-2.14.0.rc1/lib/rspec/core/example.rb:111:in `run'
5: gems/rspec-core-2.14.0.rc1/lib/rspec/core/example_group.rb:390:in `block in run_examples'
6: gems/rspec-core-2.14.0.rc1/lib/rspec/core/example_group.rb:386:in `map'
7: gems/rspec-core-2.14.0.rc1/lib/rspec/core/example_group.rb:386:in `run_examples'
   …

@pda
Copy link
Contributor Author

pda commented Jun 22, 2013

I believe this is now a working and tested version of RSpec::Mocks.allow_message and .expect_message.
Let me know if you feel anything is lacking or wrong in syntax_agnostic_message_matchers_spec.rb.
Otherwise I'll take a look at getting it into rspec/rspec-rails#764, and also revisit TestDouble#assign_stubs from #306.

@JonRowe
Copy link
Member

JonRowe commented Jun 22, 2013

Does this include exposing the any instance varieties?

@alindeman
Copy link
Contributor

  • What about should_not_receive? How do you add a negative message expectation?
  • Should stub and should_receive delegate to this new interface? We'd keep the knowledge of proxy_for in a few less places.
  • Should we add a bit of rdoc/yard docs to these methods?

@myronmarston
Copy link
Member

Does this include exposing the any instance varieties?

We could extend it to expose those, but, IMO, it's not needed. any_instance is a code smell (again, IMO) that I think is best limited to use in situations where you are getting legacy code under test for the first time. On new, greenfield code, I recommend avoiding it. As such, I think it's OK not to expose another API for it. In addition, I see this API primarily existing for use by libraries, when libraries need to set some message expectations for a user (such as rspec-rails mock_model). I think any_instance in a library is an even bigger code smell than it is in a test, and I don't really want to encourage libraries to use it. So I'm OK not including the any instance varieties.

What about should_not_receive? How do you add a negative message expectation?

I think RSpec::Mocks.expect_message(object, :message).never will work.

Should stub and should_receive delegate to this new interface? We'd keep the knowledge of proxy_for in a few less places.

I like this idea.

Should we add a bit of rdoc/yard docs to these methods?

Yep. I don't think we need a cuke for it, though; as I mentioned above, this is meant more for libraries to build off of than for end users, and the yard docs are sufficient for the purpose, IMO.

@ghost ghost assigned fables-tales Jun 28, 2013
@fables-tales
Copy link
Member

I'm going to try and close this one out.

@alindeman
Copy link
Contributor

Thanks @samphippen. I was thinking of doing it too, but got bogged down elsewhere yesterday evening. Let me know if I can help.

@fables-tales
Copy link
Member

@pda: this is looking really good, thanks for the contribution!

@pda can you add some yard/rdoc for the methods you added as per @alindeman's suggestion.

What about should_not_receive? How do you add a negative message expectation?

I think RSpec::Mocks.expect_message(object, :message).never will work.

@myronmarston: I think we should have a spec for this (that is negative expectations). @pda could you add one? If not I'll add one when I merge it in.

I guess the spec would be something like "expect message works with never".

Thanks!

@alindeman
Copy link
Contributor

Also, though, if @pda is not able to get to these things, we can probably take it from here? Just let us know.

@fables-tales
Copy link
Member

Here's what I'm thinking for the yard docs: https://gist.github.com/samphippen/5892183. @myronmarston got opinions?

@fables-tales
Copy link
Member

@fables-tales
Copy link
Member

I've finished this up for the old syntax. Can I get some review on: fcbbaba please.

@fables-tales
Copy link
Member

Ok, so all the tests aren't passing. Let me tinker.

@fables-tales
Copy link
Member

I guess this branch: https://github.com/rspec/rspec-mocks/compare/syntax_agnostic_message_matchers is actually the one that needs reviewing. If anyone could take a look and tell me what they think. It's not the most elegant solution, but the specs now at least all pass (locally).

@myronmarston
Copy link
Member

I have some comments to leave on that branch but github only allows comments on individual commits or an a PR, so I made a PR out of it: #334.

Closing this one so we can wrap things up there. @pda -- thanks for getting the ball rolling on this!

fables-tales pushed a commit that referenced this pull request Jul 1, 2013
fables-tales pushed a commit that referenced this pull request Jul 3, 2013
@samphippen's wrap up of #311: syntax agnostic message matchers
fables-tales pushed a commit that referenced this pull request Jul 3, 2013
See pull: #311 and #334.

Signed-off-by: Sam Phippen <[email protected]>

Conflicts:
	Changelog.md
	lib/rspec/mocks/message_expectation.rb
	spec/rspec/mocks/mock_spec.rb
fables-tales pushed a commit that referenced this pull request Jul 3, 2013
See pull: #311 and #334.

Signed-off-by: Sam Phippen <[email protected]>

Conflicts:
	Changelog.md
	lib/rspec/mocks/message_expectation.rb
	spec/rspec/mocks/mock_spec.rb
@pda
Copy link
Contributor Author

pda commented Jul 4, 2013

Sorry to have vanished half way though this.
I'm moving from Australia to San Francisco in a few weeks, and flat out with work on top of that!
Great to see it finished in #334, thanks @samphippen, @myronmarston and @alindeman.

yujinakayama pushed a commit to yujinakayama/rspec-monorepo that referenced this pull request Oct 6, 2021
See pull: rspec/rspec-mocks#311 and rspec/rspec-mocks#334.

Signed-off-by: Sam Phippen <[email protected]>

Conflicts:
	Changelog.md
	lib/rspec/mocks/message_expectation.rb
	spec/rspec/mocks/mock_spec.rb

---
This commit was imported from rspec/rspec-mocks@0b2e5b2.
yujinakayama pushed a commit to yujinakayama/rspec-monorepo that referenced this pull request Oct 6, 2021
See pull: rspec/rspec-mocks#311 and rspec/rspec-mocks#334.

Signed-off-by: Sam Phippen <[email protected]>

Conflicts:
	Changelog.md
	lib/rspec/mocks/message_expectation.rb
	spec/rspec/mocks/mock_spec.rb

---
This commit was imported from rspec/rspec-mocks@58488d8.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants