1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
Feature: `yield` matchers
There are four related matchers that allow you to specify whether or not a method yields,
how many times it yields, whether or not it yields with arguments, and what those
arguments are.
* `yield_control` matches if the method-under-test yields, regardless of whether or not
arguments are yielded.
* `yield_with_args` matches if the method-under-test yields with arguments. If arguments
are provided to this matcher, it will only pass if the actual yielded arguments match the expected ones using `===` or `==`.
* `yield_with_no_args` matches if the method-under-test yields with no arguments.
* `yield_successive_args` is designed for iterators, and will match if the method-under-test
yields the same number of times as arguments passed to this matcher, and all actual yielded arguments match the expected ones using `===` or `==`.
Note: your expect block _must_ accept an argument that is then passed on to the
method-under-test as a block. This acts as a "probe" that allows the matcher to detect
whether or not your method yields, and, if so, how many times and what the yielded
arguments are.
Background:
Given a file named "my_class.rb" with:
"""ruby
class MyClass
def self.yield_once_with(*args)
yield *args
end
def self.yield_twice_with(*args)
2.times { yield *args }
end
def self.raw_yield
yield
end
def self.dont_yield
end
end
"""
Scenario: The `yield_control` matcher
Given a file named "yield_control_spec.rb" with:
"""ruby
require './my_class'
RSpec.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:
| 13 examples, 7 failures |
| expected given block to yield control |
| expected given block not to yield control |
| expected given block not to yield control at least twice |
| expected given block not to yield control at most 3 times |
Scenario: The `yield_with_args` matcher
Given a file named "yield_with_args_spec.rb" with:
"""ruby
require './my_class'
RSpec.describe "yield_with_args matcher" do
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.to yield_with_args }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.to yield_with_args("foo") }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.to yield_with_args(String) }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.to yield_with_args(/oo/) }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.to yield_with_args("foo", "bar") }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.to yield_with_args(String, String) }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.to yield_with_args(/fo/, /ar/) }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.not_to yield_with_args(17, "baz") }
# deliberate failures
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.not_to yield_with_args }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.not_to yield_with_args("foo") }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.not_to yield_with_args(String) }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.not_to yield_with_args(/oo/) }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.not_to yield_with_args("foo", "bar") }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.to yield_with_args(17, "baz") }
end
"""
When I run `rspec yield_with_args_spec.rb`
Then the output should contain all of these:
| 14 examples, 6 failures |
| expected given block not to yield with arguments, but did |
| expected given block not to yield with arguments, but yielded with expected arguments |
| expected given block to yield with arguments, but yielded with unexpected arguments |
Scenario: The `yield_with_no_args` matcher
Given a file named "yield_with_no_args_spec.rb" with:
"""ruby
require './my_class'
RSpec.describe "yield_with_no_args matcher" do
specify { expect { |b| MyClass.raw_yield(&b) }.to yield_with_no_args }
specify { expect { |b| MyClass.dont_yield(&b) }.not_to yield_with_no_args }
specify { expect { |b| MyClass.yield_once_with("a", &b) }.not_to yield_with_no_args }
# deliberate failures
specify { expect { |b| MyClass.raw_yield(&b) }.not_to yield_with_no_args }
specify { expect { |b| MyClass.dont_yield(&b) }.to yield_with_no_args }
specify { expect { |b| MyClass.yield_once_with("a", &b) }.to yield_with_no_args }
end
"""
When I run `rspec yield_with_no_args_spec.rb`
Then the output should contain all of these:
| 6 examples, 3 failures |
| expected given block not to yield with no arguments, but did |
| expected given block to yield with no arguments, but did not yield |
| expected given block to yield with no arguments, but yielded with arguments: ["a"] |
Scenario: The `yield_successive_args` matcher
Given a file named "yield_successive_args_spec.rb" with:
"""ruby
def array
[1, 2, 3]
end
def array_of_tuples
[[:a, :b], [:c, :d]]
end
RSpec.describe "yield_successive_args matcher" do
specify { expect { |b| array.each(&b) }.to yield_successive_args(1, 2, 3) }
specify { expect { |b| array_of_tuples.each(&b) }.to yield_successive_args([:a, :b], [:c, :d]) }
specify { expect { |b| array.each(&b) }.to yield_successive_args(Integer, Integer, Integer) }
specify { expect { |b| array.each(&b) }.not_to yield_successive_args(1, 2) }
# deliberate failures
specify { expect { |b| array.each(&b) }.not_to yield_successive_args(1, 2, 3) }
specify { expect { |b| array_of_tuples.each(&b) }.not_to yield_successive_args([:a, :b], [:c, :d]) }
specify { expect { |b| array.each(&b) }.not_to yield_successive_args(Integer, Integer, Integer) }
specify { expect { |b| array.each(&b) }.to yield_successive_args(1, 2) }
end
"""
When I run `rspec yield_successive_args_spec.rb`
Then the output should contain all of these:
| 8 examples, 4 failures |
| expected given block not to yield successively with arguments, but yielded with expected arguments |
| expected given block to yield successively with arguments, but yielded with unexpected arguments |
|