Skip to content

Instantly share code, notes, and snippets.

@myronmarston
Created March 17, 2012 21:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save myronmarston/2065445 to your computer and use it in GitHub Desktop.
Save myronmarston/2065445 to your computer and use it in GitHub Desktop.
Proof of concept for yield_value rspec matcher
RSpec::Matchers.define :yield_value do |expected_yielded_value|
match do |block|
yielded_value = nil
block.call(lambda { |arg| yielded_value = arg })
yielded_value == expected_yielded_value
end
end
describe "yield_value matcher" do
it 'passes a valid positive expectation' do
expect { |b| 3.tap(&b) }.to yield_value(3)
end
it 'fails an invalid positive expectation' do
expect {
expect { |b| 3.tap(&b) }.to yield_value(2)
}.to raise_error(RSpec::Expectations::ExpectationNotMetError)
end
end
@mattwynne
Copy link

I like where this is heading. For multiple yields (e.g. Array#each), what about being able to chain calls to construct more matchers, like this:

expect { |b| [1, 2, 3].each(&b) }.to yield_args(1).then(2).then(3)

@dchelimsky
Copy link

@mattwynne nice solution! One less matcher. We should do the same in rspec-mocks:

object.stub(:method).and_return(x).then(y).then(z)

@myronmarston
Copy link
Author

@mattwynne: I like that as a solution for short arrays (of one or two items), but imagine doing that on an array of 100 items. It doesn't scale to larger arrays. Or consider trying to write a helper method that accepts an array as an argument, and after doing a bunch of stuff, needs to make an assertion that all the items in the array were yielded. With that approach, the helper method would have to use a messy #inject:

first_arg = array.shift
matcher = array.inject(yield_args(first_arg)) do |matcher, arg|
  matcher.then(arg)
end

expect { |b| my_object_that_wraps_the_array.each(&b) }.to matcher

This would be much, much cleaner with a dedicated matcher for the multi-yield case.

@myronmarston
Copy link
Author

BTW, on the topic of the naming of the matchers, I think I like yield_with_args better than yield_args. It has a nice symmetry to yield_without_args, and it's more closely aligned with how I talk about this stuff: "3.tap yields with 3 as the argument".

@myronmarston
Copy link
Author

I'll start taking a stab at this, and open a pull request with what I come up with.

@myronmarston
Copy link
Author

FWIW, I've pushed what I've got so far into a branch:

https://github.com/rspec/rspec-expectations/tree/yield_matchers

There's more I plan to do before submitting a pull request, but I figured I'd push something to back it up and so people who care to can take a look.

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