-
-
Save myronmarston/2065445 to your computer and use it in GitHub Desktop.
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 |
@dchelimsky: I see your point about yield_arg/yield_args and agree. Same goes for yield_without_args and yield_control. I hadn't thought of it that way.
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)
@mattwynne nice solution! One less matcher. We should do the same in rspec-mocks:
object.stub(:method).and_return(x).then(y).then(z)
@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.
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".
I'll start taking a stab at this, and open a pull request with what I come up with.
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.
I like
arg
vvalue
. Not sure we need bothyield_arg
andyield_args
. I thinkyield_args(with_one_arg)
is perfectly readable.re:
yield_without_args
vyield_control
, these mean two different things: "yield without args" v "yield control, but I don't care if there are args or not". Thinking this through, I think we should have both, since the latter supports a looser coupling that might be appropriate in some cases.