rbehave 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.txt +2 -0
- data/History.txt +0 -0
- data/Manifest.txt +31 -0
- data/NOTES.txt +11 -0
- data/README.txt +4 -0
- data/Rakefile +54 -0
- data/behaviour/everything.rb +1 -0
- data/behaviour/examples/everything.rb +3 -0
- data/behaviour/examples/helper.rb +2 -0
- data/behaviour/examples/rbehave/documenter/plain_text_documenter_behaviour.rb +58 -0
- data/behaviour/examples/rbehave/exception_behaviour.rb +27 -0
- data/behaviour/examples/rbehave/reporter/plain_text_reporter_behaviour.rb +112 -0
- data/behaviour/examples/rbehave/runner/scenario_collector_behaviour.rb +25 -0
- data/behaviour/examples/rbehave/runner/scenario_runner_behaviour.rb +143 -0
- data/behaviour/examples/rbehave/runner/story_runner_behaviour.rb +169 -0
- data/behaviour/examples/rbehave/story_behaviour.rb +20 -0
- data/behaviour/examples/rbehave/world_behaviour.rb +259 -0
- data/behaviour/examples/rspec_adapter.rb +81 -0
- data/lib/rbehave/documenter/plain_text_documenter.rb +29 -0
- data/lib/rbehave/exceptions.rb +24 -0
- data/lib/rbehave/reporter/plain_text_reporter.rb +58 -0
- data/lib/rbehave/runner/scenario_collector.rb +16 -0
- data/lib/rbehave/runner/scenario_runner.rb +40 -0
- data/lib/rbehave/runner/story_runner.rb +44 -0
- data/lib/rbehave/scenario.rb +11 -0
- data/lib/rbehave/story.rb +15 -0
- data/lib/rbehave/version.rb +9 -0
- data/lib/rbehave/world.rb +55 -0
- data/lib/rbehave.rb +72 -0
- data/setup.rb +1585 -0
- metadata +75 -0
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'behaviour/examples/helper'
|
2
|
+
|
3
|
+
module RBehave
|
4
|
+
describe World do
|
5
|
+
setup do
|
6
|
+
World.listeners.clear
|
7
|
+
RBehave::Runner.dry_run = false
|
8
|
+
end
|
9
|
+
|
10
|
+
teardown do
|
11
|
+
World.listeners.clear
|
12
|
+
RBehave::Runner.dry_run = false
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should create an object that mixes in a World' do
|
16
|
+
# when
|
17
|
+
obj = World::create
|
18
|
+
|
19
|
+
# then
|
20
|
+
obj.should be_kind_of(World)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should create an object of a specified type that mixes in a world' do
|
24
|
+
# when
|
25
|
+
obj = World::create String
|
26
|
+
|
27
|
+
# then
|
28
|
+
obj.should be_kind_of(String)
|
29
|
+
obj.should be_kind_of(World)
|
30
|
+
end
|
31
|
+
|
32
|
+
def ensure_world_executes_step(&block)
|
33
|
+
# given
|
34
|
+
obj = World::create
|
35
|
+
$step_ran = false
|
36
|
+
|
37
|
+
# when
|
38
|
+
obj.instance_eval(&block)
|
39
|
+
|
40
|
+
# then
|
41
|
+
$step_ran.should be_true
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should execute a Given, When or Then step' do
|
45
|
+
ensure_world_executes_step do
|
46
|
+
Given 'a given' do
|
47
|
+
$step_ran = true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
ensure_world_executes_step do
|
52
|
+
When 'an event' do
|
53
|
+
$step_ran = true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
ensure_world_executes_step do
|
58
|
+
Then 'an outcome' do
|
59
|
+
$step_ran = true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should reuse a given across scenarios' do
|
65
|
+
# given
|
66
|
+
$num_invoked = 0
|
67
|
+
a_world = World::create
|
68
|
+
a_world.instance_eval do
|
69
|
+
Given 'a given' do
|
70
|
+
$num_invoked += 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
another_world = World::create
|
74
|
+
|
75
|
+
# when
|
76
|
+
another_world.instance_eval do
|
77
|
+
Given 'a given' # without a body
|
78
|
+
end
|
79
|
+
|
80
|
+
# then
|
81
|
+
$num_invoked.should == 2
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should reuse an event across scenarios' do
|
85
|
+
# given
|
86
|
+
$num_invoked = 0
|
87
|
+
a_world = World::create
|
88
|
+
a_world.instance_eval do
|
89
|
+
When 'an event' do
|
90
|
+
$num_invoked += 1
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
another_world = World::create
|
95
|
+
|
96
|
+
# when
|
97
|
+
another_world.instance_eval do
|
98
|
+
When 'an event' # without a body
|
99
|
+
end
|
100
|
+
|
101
|
+
# then
|
102
|
+
$num_invoked.should == 2
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should reuse an outcome across scenarios' do
|
106
|
+
# given
|
107
|
+
$num_invoked = 0
|
108
|
+
a_world = World::create
|
109
|
+
a_world.instance_eval do
|
110
|
+
Then 'an outcome' do
|
111
|
+
$num_invoked += 1
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
another_world = World::create
|
116
|
+
|
117
|
+
# when
|
118
|
+
another_world.instance_eval do
|
119
|
+
Then 'an outcome' # without a body
|
120
|
+
end
|
121
|
+
|
122
|
+
# then
|
123
|
+
$num_invoked.should == 2
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should preserve instance variables between steps within a scenario' do
|
127
|
+
# given
|
128
|
+
world = World::create
|
129
|
+
$first = nil
|
130
|
+
$second = nil
|
131
|
+
|
132
|
+
# when
|
133
|
+
world.instance_eval do
|
134
|
+
Given 'given' do
|
135
|
+
@first = 'first'
|
136
|
+
end
|
137
|
+
When 'event' do
|
138
|
+
@second = @first # from given
|
139
|
+
end
|
140
|
+
Then 'outcome' do
|
141
|
+
$first = @first # from given
|
142
|
+
$second = @second # from event
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# then
|
147
|
+
ensure_that $first, is('first')
|
148
|
+
ensure_that $second, is('first')
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should invoke the reused step in the new object instance' do
|
152
|
+
# given
|
153
|
+
$instances = []
|
154
|
+
world1 = World.create
|
155
|
+
world1.instance_eval do
|
156
|
+
Given 'a given' do
|
157
|
+
$instances << self
|
158
|
+
end
|
159
|
+
end
|
160
|
+
world2 = World.create
|
161
|
+
|
162
|
+
# when
|
163
|
+
world2.instance_eval do
|
164
|
+
Given 'a given' # reused
|
165
|
+
Then 'an outcome' do
|
166
|
+
$instances << self
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# then
|
171
|
+
$instances.should == [ world1, world2, world2 ]
|
172
|
+
end
|
173
|
+
|
174
|
+
def ensure_world_propagates_error(expected_error, &block)
|
175
|
+
# given
|
176
|
+
world = World.create
|
177
|
+
$error = nil
|
178
|
+
|
179
|
+
# when
|
180
|
+
error = exception_from do
|
181
|
+
world.instance_eval(&block)
|
182
|
+
end
|
183
|
+
|
184
|
+
# then
|
185
|
+
error.should be_kind_of(expected_error)
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'should propagate a failure from a Given, When or Then step' do
|
189
|
+
ensure_world_propagates_error RuntimeError do
|
190
|
+
Given 'a given' do
|
191
|
+
raise RuntimeError, "oops"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
ensure_world_propagates_error RuntimeError do
|
196
|
+
When 'an event' do
|
197
|
+
raise RuntimeError, "oops"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
ensure_world_propagates_error RuntimeError do
|
202
|
+
Then 'an outcome' do
|
203
|
+
raise RuntimeError, "oops"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'should inform listeners when it runs a Given, When or Then step' do
|
209
|
+
# given
|
210
|
+
world = World.create
|
211
|
+
mock_listener1 = mock('listener1')
|
212
|
+
mock_listener2 = mock('listener2')
|
213
|
+
World.add_listener(mock_listener1)
|
214
|
+
World.add_listener(mock_listener2)
|
215
|
+
|
216
|
+
# expect
|
217
|
+
mock_listener1.expects(:found_step).with(:given, 'a context')
|
218
|
+
mock_listener1.expects(:found_step).with(:when, 'an event')
|
219
|
+
mock_listener1.expects(:found_step).with(:then, 'an outcome')
|
220
|
+
|
221
|
+
mock_listener2.expects(:found_step).with(:given, 'a context')
|
222
|
+
mock_listener2.expects(:found_step).with(:when, 'an event')
|
223
|
+
mock_listener2.expects(:found_step).with(:then, 'an outcome')
|
224
|
+
|
225
|
+
# when
|
226
|
+
world.instance_eval do
|
227
|
+
Given 'a context' do end
|
228
|
+
When 'an event' do end
|
229
|
+
Then 'an outcome' do end
|
230
|
+
end
|
231
|
+
|
232
|
+
# then
|
233
|
+
verify_mocks
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'should tell listeners but not execute the step if it is in dry-run mode' do
|
237
|
+
# given
|
238
|
+
RBehave::Runner.dry_run = true
|
239
|
+
mock_listener = mock('listener')
|
240
|
+
World.add_listener(mock_listener)
|
241
|
+
$step_invoked = false
|
242
|
+
world = World.create
|
243
|
+
|
244
|
+
# expect
|
245
|
+
mock_listener.expects(:found_step).with(:given, 'a context')
|
246
|
+
|
247
|
+
# when
|
248
|
+
world.instance_eval do
|
249
|
+
Given 'a context' do
|
250
|
+
$step_invoked = true
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# then
|
255
|
+
verify_mocks
|
256
|
+
$step_invoked.should be(false)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require 'mocha'
|
4
|
+
|
5
|
+
# replace rspec's mocks with mocha
|
6
|
+
Spec::Runner::configuration.mock_with :mocha
|
7
|
+
|
8
|
+
# add ensure_that(..), verify(..)
|
9
|
+
module Spec::Expectations::ObjectExpectations
|
10
|
+
def ensure_that(obj, expr)
|
11
|
+
obj.should expr
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# helper methods
|
16
|
+
def pending(message = "todo")
|
17
|
+
where = caller[0]
|
18
|
+
at_exit { puts "#{where}: #{message}" }
|
19
|
+
end
|
20
|
+
|
21
|
+
def exception_from(&block)
|
22
|
+
begin
|
23
|
+
yield
|
24
|
+
rescue StandardError => e
|
25
|
+
e
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# simplify matchers
|
30
|
+
|
31
|
+
class Matcher
|
32
|
+
def initialize(description, &match_block)
|
33
|
+
@description = description
|
34
|
+
@match_block = match_block
|
35
|
+
end
|
36
|
+
|
37
|
+
def matches?(actual)
|
38
|
+
@actual = actual
|
39
|
+
return @match_block.call(actual)
|
40
|
+
end
|
41
|
+
|
42
|
+
def failure_message()
|
43
|
+
return %[Expected #@description but got #@actual]
|
44
|
+
end
|
45
|
+
|
46
|
+
def negative_failure_message()
|
47
|
+
return %[Expected not to get #@description, but got #@actual]
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
failure_message
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# custom matchers
|
56
|
+
|
57
|
+
def contain(string)
|
58
|
+
return Matcher.new(%[string containing "#{string}"]) do |actual|
|
59
|
+
actual.include? string
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
alias :contains :contain
|
64
|
+
|
65
|
+
def is(expected)
|
66
|
+
return Matcher.new("equal to #{expected}") do |actual| actual == expected end
|
67
|
+
end
|
68
|
+
|
69
|
+
alias :are :is
|
70
|
+
|
71
|
+
def is_a(type)
|
72
|
+
return Matcher.new("object of type #{type}") do |actual|
|
73
|
+
actual.is_a? type
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def matches(pattern)
|
78
|
+
return Matcher.new("string matching #{pattern}") do |actual|
|
79
|
+
actual =~ pattern
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RBehave
|
2
|
+
module Documenter
|
3
|
+
class PlainTextDocumenter
|
4
|
+
def initialize(out)
|
5
|
+
@out = out
|
6
|
+
end
|
7
|
+
|
8
|
+
def story_started(title, narrative)
|
9
|
+
@out << "Story: #{title}\n#{narrative}\n"
|
10
|
+
end
|
11
|
+
|
12
|
+
def story_ended(title, narrative)
|
13
|
+
@out << "\n\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
def scenario_started(story_title, scenario_name)
|
17
|
+
@out << "\nScenario: #{scenario_name}\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
def found_step(name, description)
|
21
|
+
@out << " #{name.to_s.capitalize} #{description}\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(meth, *args, &block)
|
25
|
+
# ignore any other calls
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RBehave
|
2
|
+
class PendingException < StandardError
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
class Exception
|
7
|
+
class << self
|
8
|
+
def backtrace_filters
|
9
|
+
@backtrace_filters ||= []
|
10
|
+
end
|
11
|
+
def add_backtrace_filter(filter)
|
12
|
+
backtrace_filters << filter
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def filtered_backtrace
|
17
|
+
trace = backtrace || []
|
18
|
+
trace.reject do |line|
|
19
|
+
Exception.backtrace_filters.inject(false) do |already_matched, filter|
|
20
|
+
already_matched || line =~ filter
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module RBehave
|
2
|
+
module Reporter
|
3
|
+
class PlainTextReporter
|
4
|
+
def initialize(out)
|
5
|
+
@out = out
|
6
|
+
@succeeded = 0
|
7
|
+
@failed = []
|
8
|
+
@pending = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def scenario_succeeded(story_title, scenario_name)
|
12
|
+
@out << '.'
|
13
|
+
@succeeded += 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def scenario_failed(story_title, scenario_name, err)
|
17
|
+
@out << 'F'
|
18
|
+
@failed << [story_title, scenario_name, err]
|
19
|
+
end
|
20
|
+
|
21
|
+
def scenario_pending(story_title, scenario_name, msg)
|
22
|
+
@pending << [story_title, scenario_name, msg]
|
23
|
+
@out << 'P'
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_started(count)
|
27
|
+
@count = count
|
28
|
+
@out << "Running #@count scenarios:\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_ended
|
32
|
+
@out << "\n\n#@count scenarios: #@succeeded succeeded, #{@failed.size} failed, #{@pending.size} pending\n"
|
33
|
+
unless @pending.empty?
|
34
|
+
@out << "\nPending:\n"
|
35
|
+
@pending.each_with_index do |pending, i|
|
36
|
+
title, scenario_name, msg = pending
|
37
|
+
@out << "#{i+1}) #{title} (#{scenario_name}): #{msg}\n"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
unless @failed.empty?
|
41
|
+
@out << "\nFAILURES:"
|
42
|
+
@failed.each_with_index do |failure, i|
|
43
|
+
title, scenario_name, err = failure
|
44
|
+
@out << %[
|
45
|
+
#{i+1}) #{title} (#{scenario_name}) FAILED
|
46
|
+
#{err.class}: #{err.message}
|
47
|
+
#{err.filtered_backtrace.join("\n")}
|
48
|
+
]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def method_missing(meth, *args, &block)
|
54
|
+
# ignore unexpected callbacks
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module RBehave
|
2
|
+
module Runner
|
3
|
+
class ScenarioCollector
|
4
|
+
attr_accessor :scenarios
|
5
|
+
|
6
|
+
def initialize(story)
|
7
|
+
@story = story
|
8
|
+
@scenarios = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def Scenario(name, &body)
|
12
|
+
@scenarios << Scenario.new(@story, name, &body)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module RBehave
|
2
|
+
module Runner
|
3
|
+
class ScenarioRunner
|
4
|
+
def initialize
|
5
|
+
@listeners = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def run(scenario, world)
|
9
|
+
begin
|
10
|
+
@listeners.each { |l| l.scenario_started(scenario.story.title, scenario.name) }
|
11
|
+
run_story_ignoring_scenarios(scenario.story, world)
|
12
|
+
world.instance_eval(&scenario.body)
|
13
|
+
@listeners.each { |l| l.scenario_succeeded(scenario.story.title, scenario.name) }
|
14
|
+
rescue PendingException => e
|
15
|
+
@listeners.each { |l| l.scenario_pending(scenario.story.title, scenario.name, e.message) }
|
16
|
+
rescue StandardError => e
|
17
|
+
@listeners.each { |l| l.scenario_failed(scenario.story.title, scenario.name, e) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_listener(listener)
|
22
|
+
@listeners << listener
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def run_story_ignoring_scenarios(story, world)
|
28
|
+
class << world
|
29
|
+
def Scenario(name, &block)
|
30
|
+
# do nothing
|
31
|
+
end
|
32
|
+
end
|
33
|
+
story.run_in(world)
|
34
|
+
class << world
|
35
|
+
remove_method(:Scenario)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RBehave
|
2
|
+
module Runner
|
3
|
+
class StoryRunner
|
4
|
+
attr_accessor :stories, :scenarios
|
5
|
+
|
6
|
+
def initialize(scenario_runner, world_creator = World)
|
7
|
+
@scenario_runner = scenario_runner
|
8
|
+
@world_creator = world_creator
|
9
|
+
@stories = []
|
10
|
+
@scenarios_by_story = {}
|
11
|
+
@scenarios = []
|
12
|
+
@listeners = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def Story(title, narrative, &body)
|
16
|
+
story = Story.new(title, narrative, &body)
|
17
|
+
@stories << story
|
18
|
+
|
19
|
+
# collect scenarios
|
20
|
+
collector = ScenarioCollector.new(story)
|
21
|
+
story.run_in(collector)
|
22
|
+
@scenarios += collector.scenarios
|
23
|
+
@scenarios_by_story[story.title] = collector.scenarios
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_stories
|
27
|
+
@listeners.each { |l| l.run_started(scenarios.size) }
|
28
|
+
@stories.each do |story|
|
29
|
+
@listeners.each { |l| l.story_started(story.title, story.narrative) }
|
30
|
+
scenarios = @scenarios_by_story[story.title]
|
31
|
+
scenarios.each do |scenario|
|
32
|
+
@scenario_runner.run(scenario, @world_creator.create)
|
33
|
+
end
|
34
|
+
@listeners.each { |l| l.story_ended(story.title, story.narrative) }
|
35
|
+
end
|
36
|
+
@listeners.each { |l| l.run_ended }
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_listener(listener)
|
40
|
+
@listeners << listener
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module RBehave
|
2
|
+
class Story
|
3
|
+
attr_reader :title, :narrative
|
4
|
+
|
5
|
+
def initialize(title, narrative, &body)
|
6
|
+
@body = body
|
7
|
+
@title = title
|
8
|
+
@narrative = narrative
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_in(container)
|
12
|
+
container.instance_eval(&@body)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RBehave
|
2
|
+
=begin
|
3
|
+
A World represents the actual instance a scenario will run in.
|
4
|
+
|
5
|
+
RBehave ensures any instance variables and methods defined anywhere
|
6
|
+
in a story block are available to all the scenarios. This includes
|
7
|
+
variables that are created or referenced inside Given, When and Then
|
8
|
+
blocks.
|
9
|
+
=end
|
10
|
+
module World
|
11
|
+
# store steps and listeners in the singleton metaclass.
|
12
|
+
# This serves both to keep them out of the way and to
|
13
|
+
# make them available to all instances.
|
14
|
+
class << self
|
15
|
+
def create(cls = Object)
|
16
|
+
cls.new.extend(World)
|
17
|
+
end
|
18
|
+
|
19
|
+
def store_and_call(instance, type, name, &block)
|
20
|
+
@step_mother ||= Hash.new {|hsh,key| hsh[key] = Hash.new }
|
21
|
+
if block_given?
|
22
|
+
@step_mother[type][name] = block
|
23
|
+
else
|
24
|
+
block = @step_mother[type][name]
|
25
|
+
end
|
26
|
+
listeners.each { |l| l.found_step(type, name) }
|
27
|
+
instance.instance_eval(&block) unless RBehave::Runner.dry_run?
|
28
|
+
end
|
29
|
+
|
30
|
+
def listeners
|
31
|
+
@listeners ||= []
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_listener(listener)
|
35
|
+
listeners << listener
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def Given(name, &block)
|
40
|
+
World.store_and_call self, :given, name, &block
|
41
|
+
end
|
42
|
+
|
43
|
+
def When(name, &block)
|
44
|
+
World.store_and_call self, :when, name, &block
|
45
|
+
end
|
46
|
+
|
47
|
+
def Then(name, &block)
|
48
|
+
World.store_and_call self, :then, name, &block
|
49
|
+
end
|
50
|
+
|
51
|
+
def pending(message = 'todo')
|
52
|
+
raise PendingException, message
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|