Permalink
Browse files

Merge pull request #217 from samuil/master

yield_control matchers could allow to define yields count
  • Loading branch information...
2 parents 7194b33 + 0619622 commit 0490bb3bbd82bff4fff67264d14f98ea308b57c9 @myronmarston myronmarston committed Mar 11, 2013
Showing with 131 additions and 10 deletions.
  1. +18 −3 features/built_in_matchers/yield.feature
  2. +63 −3 lib/rspec/matchers/built_in/yield.rb
  3. +50 −4 spec/rspec/matchers/yield_spec.rb
@@ -30,6 +30,10 @@ Feature: yield matchers
yield *args
end
+ def self.yield_twice_with(*args)
+ 2.times { yield *args }
+ end
+
def self.raw_yield
yield
end
@@ -47,17 +51,28 @@ Feature: yield matchers
describe "yield_control matcher" do
specify { expect { |b| MyClass.yield_once_with(1, &b) }.to yield_control }
specify { expect { |b| MyClass.dont_yield(&b) }.not_to yield_control }
+ specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.twice }
+ specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.exactly(2).times }
+ specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.at_least(1) }
+ specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.at_most(3).times }
# deliberate failures
specify { expect { |b| MyClass.yield_once_with(1, &b) }.not_to yield_control }
specify { expect { |b| MyClass.dont_yield(&b) }.to yield_control }
+ specify { expect { |b| MyClass.yield_once_with(1, &b) }.to yield_control.at_least(2).times }
+ specify { expect { |b| MyClass.yield_twice_with(1, &b) }.not_to yield_control.twice }
+ specify { expect { |b| MyClass.yield_twice_with(1, &b) }.not_to yield_control.at_least(2).times }
+ specify { expect { |b| MyClass.yield_twice_with(1, &b) }.not_to yield_control.at_least(1) }
+ specify { expect { |b| MyClass.yield_twice_with(1, &b) }.not_to yield_control.at_most(3).times }
end
"""
When I run `rspec yield_control_spec.rb`
Then the output should contain all of these:
- | 4 examples, 2 failures |
- | expected given block to yield control |
- | expected given block not to yield control |
+ | 13 examples, 7 failures |
+ | expected given block to yield control |
+ | expected given block not to yield control |
+ | expected given block not to yield control 2 or more times |
+ | expected given block not to yield control 3 or less times |
Scenario: yield_with_args matcher
Given a file named "yield_with_args_spec.rb" with:
@@ -65,17 +65,77 @@ def self.assert_valid_expect_block!(block)
end
class YieldControl < BaseMatcher
+ def initialize
+ @expectation_type = :==
+ @expected_yields_count = 1
+ end
+
def matches?(block)
probe = YieldProbe.probe(block)
- probe.yielded_once?(:yield_control)
+ probe.num_yields.send(@expectation_type, @expected_yields_count)
+ end
+
+ def twice
+ exactly(2).times
+ self
+ end
+
+ def exactly(number)
+ set_expected_yields_count(:==, number)
+ self
+ end
+
+ def at_most(number)
+ set_expected_yields_count(:<=, number)
+ self
+ end
+
+ def at_least(number)
+ set_expected_yields_count(:>=, number)
+ self
+ end
+
+ def times
+ self
end
def failure_message_for_should
- "expected given block to yield control"
+ 'expected given block to yield control'.tap do |failure_message|
+ failure_message << relativity_failure_message
+ end
end
def failure_message_for_should_not
- "expected given block not to yield control"
+ 'expected given block not to yield control'.tap do |failure_message|
+ failure_message << relativity_failure_message
+ end
+ end
+
+ private
+
+ def set_expected_yields_count(relativity, n)
+ @expectation_type = relativity
+ @expected_yields_count = case n
+ when Numeric then n
+ when :once then 1
+ when :twice then 2
+ end
+ end
+
+ def relativity_failure_message
+ if @expected_yields_count != 1
+ " #@expected_yields_count #{human_readable_expecation_type}times"
+ else
+ ''
+ end
+ end
+
+ def human_readable_expecation_type
+ case @expectation_type
+ when :<= then 'or less '
+ when :>= then 'or more '
+ else ''
+ end
end
end
@@ -63,10 +63,56 @@ def each_arg(*args, &block)
}.to fail_with(/expected given block to yield control/)
end
- it 'raises an error if it yields multiple times' do
- expect {
- expect { |b| [1, 2].each(&b) }.to yield_control
- }.to raise_error(/not designed.*yields multiple times/)
+ context "with exact count" do
+ it 'fails if the block yields wrong number of times' do
+ expect {
+ expect { |b| [1, 2, 3].each(&b) }.to yield_control.twice
+ }.to fail_with(/expected given block to yield control 2 times/)
+ end
+
+ it 'passes if the block yields twice' do
+ expect { |b| [1, 2].each(&b) }.to yield_control.twice
+ end
+
+ it 'passes if the block yields exactly 3 times' do
+ expect { |b| [1, 2, 3].each(&b) }.to yield_control.exactly(3).times
+ end
+ end
+
+ context "with at_least count" do
+ it 'passes if the block yields twice' do
+ expect { |b| [1, 2].each(&b) }.to yield_control.at_least(2).times
+ end
+
+ it 'passes if the block yields :twice' do
+ expect { |b| [1, 2].each(&b) }.to yield_control.at_least(:twice)
+ end
+
+ it 'passes if the block yields thrice' do
+ expect { |b| [1, 2, 3].each(&b) }.to yield_control.at_least(2).times
+ end
+
+ it 'fails if the block yields once' do
+ expect {
+ expect { |b| _yield_with_no_args(&b) }.to yield_control.at_least(2).times
+ }.to fail_with(/expected given block to yield control 2 or more times/)
+ end
+ end
+
+ context "with at_most count" do
+ it 'passes if the block yields twice' do
+ expect { |b| [1, 2].each(&b) }.to yield_control.at_most(2).times
+ end
+
+ it 'passes if the block yields thrice' do
+ expect { |b| _yield_with_no_args(&b) }.to yield_control.at_most(2).times
+ end
+
+ it 'fails if the block yields once' do
+ expect {
+ expect { |b| [1, 2, 3].each(&b) }.to yield_control.at_most(2).times
+ }.to fail_with(/expected given block to yield control 2 or less times/)
+ end
end
end

0 comments on commit 0490bb3

Please sign in to comment.