turnip 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/.travis.yml +5 -0
- data/README.md +196 -44
- data/Rakefile +8 -0
- data/examples/alignment_steps.rb +7 -0
- data/examples/autoload_steps.feature +5 -0
- data/examples/autoload_steps.rb +5 -0
- data/examples/dragon_steps.rb +17 -0
- data/examples/evil_steps.rb +7 -0
- data/examples/knight_steps.rb +29 -0
- data/examples/more_steps.rb +7 -0
- data/examples/neutral_steps.rb +7 -0
- data/examples/red_dragon_steps.rb +18 -0
- data/examples/step_calling.feature +14 -0
- data/examples/step_calling_steps.rb +23 -0
- data/examples/steps.rb +0 -22
- data/examples/steps_for_super.feature +16 -0
- data/lib/turnip.rb +12 -12
- data/lib/turnip/builder.rb +20 -4
- data/lib/turnip/config.rb +18 -0
- data/lib/turnip/dsl.rb +19 -13
- data/lib/turnip/feature_file.rb +20 -0
- data/lib/turnip/loader.rb +6 -3
- data/lib/turnip/placeholder.rb +1 -1
- data/lib/turnip/runner_dsl.rb +9 -0
- data/lib/turnip/scenario_context.rb +41 -0
- data/lib/turnip/scenario_runner.rb +35 -0
- data/lib/turnip/step_definition.rb +14 -30
- data/lib/turnip/step_loader.rb +27 -0
- data/lib/turnip/step_module.rb +89 -0
- data/lib/turnip/table.rb +12 -0
- data/lib/turnip/version.rb +1 -1
- data/spec/builder_spec.rb +41 -2
- data/spec/dsl_spec.rb +22 -34
- data/spec/feature_file_spec.rb +18 -0
- data/spec/integration_spec.rb +1 -1
- data/spec/runner_dsl_spec.rb +23 -0
- data/spec/scenario_context_spec.rb +51 -0
- data/spec/scenario_runner_spec.rb +79 -0
- data/spec/spec_helper.rb +1 -3
- data/spec/step_definition_spec.rb +22 -34
- data/spec/step_loader_spec.rb +29 -0
- data/spec/step_module_spec.rb +106 -0
- data/spec/table_spec.rb +8 -1
- data/turnip.gemspec +2 -1
- metadata +51 -7
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Turnip
|
2
2
|
|
3
|
+
[![Build Status](https://secure.travis-ci.org/jnicklas/turnip.png)](http://travis-ci.org/jnicklas/turnip)
|
4
|
+
|
3
5
|
Turnip is a [Gherkin](https://github.com/cucumber/cucumber/wiki/Gherkin)
|
4
6
|
extension for RSpec. It allows you to write tests in Gherkin and run them
|
5
7
|
through your RSpec environment. Basically you can write cucumber features in
|
@@ -28,6 +30,17 @@ exist), and add the following line:
|
|
28
30
|
-r turnip
|
29
31
|
```
|
30
32
|
|
33
|
+
## Development
|
34
|
+
|
35
|
+
* Source hosted at [GitHub](http://github.com/jnicklas/turnip).
|
36
|
+
* Please direct questions, discussion or problems to the [mailing list](http://groups.google.com/group/ruby-turnip).
|
37
|
+
Please do not open an issue on GitHub if you have a question.
|
38
|
+
* If you found a reproducible bug, open a [GitHub Issue](http://github.com/jnicklas/turnip/issues) to submit a bug report.
|
39
|
+
* Please do not contact any of the maintainers directly, unless you have found a security related issue.
|
40
|
+
|
41
|
+
Pull requests are very welcome (and even better than bug reports)!
|
42
|
+
Please create a topic branch for every separate change you make.
|
43
|
+
|
31
44
|
## Compatibility
|
32
45
|
|
33
46
|
Turnip does not work on Ruby 1.8.X.
|
@@ -60,9 +73,33 @@ Yes, that's really it.
|
|
60
73
|
|
61
74
|
## Defining steps
|
62
75
|
|
63
|
-
You might want to define some steps.
|
64
|
-
|
65
|
-
|
76
|
+
You might want to define some steps. Just as in cucumber, your step files
|
77
|
+
should be named `[something]_steps.rb`. All files ending in `*steps.rb`
|
78
|
+
will be automatically required if they are under the Turnip step directory.
|
79
|
+
|
80
|
+
The default step directory for Turnip is `spec/`. You can override this
|
81
|
+
in your `spec_helper` by setting `Turnip::Config.step_dirs`. For example:
|
82
|
+
|
83
|
+
``` ruby
|
84
|
+
# spec/spec_helper.rb
|
85
|
+
RSpec.configure do |config|
|
86
|
+
Turnip::Config.step_dirs = 'examples'
|
87
|
+
Turnip::StepLoader.load_steps
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
This would set the Turnip step dirs to `examples/` and automatically load
|
92
|
+
all `*steps.rb` files anywhere under that directory.
|
93
|
+
|
94
|
+
The steps you define in your step files can be global or they can be scoped
|
95
|
+
to certain features (or scenarios)...
|
96
|
+
|
97
|
+
### Global steps
|
98
|
+
Global steps can be used by any feature/scenario you write since they are
|
99
|
+
unscoped. The names must be unique across all step files in the global
|
100
|
+
namespace.
|
101
|
+
|
102
|
+
Define them in your step file like this:
|
66
103
|
|
67
104
|
``` ruby
|
68
105
|
step "there is a monster" do
|
@@ -96,89 +133,204 @@ end
|
|
96
133
|
|
97
134
|
That will match both "there is X monster" or "there are X monsters".
|
98
135
|
|
99
|
-
|
136
|
+
You can also define custom step placeholders. More on that later.
|
137
|
+
|
138
|
+
### Scoped steps
|
139
|
+
Scoped steps help you to organize steps that are specific to
|
140
|
+
certain features or scenarios. They only need to be unique within
|
141
|
+
the scopes being used by the running scenario.
|
100
142
|
|
101
|
-
|
102
|
-
placeholders, and it is bothersome to have to constantly cast them to the
|
103
|
-
correct type. Turnip's placeholder solve both problems, like this:
|
143
|
+
To define scoped steps use `steps_for`:
|
104
144
|
|
105
145
|
``` ruby
|
106
|
-
|
107
|
-
|
146
|
+
steps_for :interface do
|
147
|
+
step "I do it" do
|
148
|
+
...
|
149
|
+
end
|
108
150
|
end
|
109
151
|
|
110
|
-
|
111
|
-
|
112
|
-
|
152
|
+
steps_for :database do
|
153
|
+
step "I do it" do
|
154
|
+
...
|
113
155
|
end
|
156
|
+
end
|
157
|
+
```
|
114
158
|
|
115
|
-
|
116
|
-
|
159
|
+
Even though the step is named the same, you can now use it in
|
160
|
+
your feature files like so:
|
161
|
+
|
162
|
+
``` cucumber
|
163
|
+
@interface
|
164
|
+
Scenario: do it through the interface
|
165
|
+
|
166
|
+
@database
|
167
|
+
Scenario: do it through the database
|
168
|
+
```
|
169
|
+
|
170
|
+
Note that this would still cause an error if you tagged a Scenario
|
171
|
+
with both `@interface` and `@database` at the same time.
|
172
|
+
|
173
|
+
Scoped steps are really just Ruby modules under the covers so you
|
174
|
+
can do anything you'd normally want to do including defining
|
175
|
+
helper/utility methods and variables. Check out
|
176
|
+
[features/alignment_steps.rb](https://github.com/jnicklas/turnip/blob/master/examples/alignment_steps.rb)
|
177
|
+
and
|
178
|
+
[features/evil_steps.rb](https://github.com/jnicklas/turnip/blob/master/examples/evil_steps.rb) for basic examples.
|
179
|
+
|
180
|
+
### Reusing steps
|
181
|
+
When using scoped steps in Turnip, you can tell it to also include steps
|
182
|
+
defined in another `steps_for` block. The syntax for that is `use_steps`:
|
183
|
+
|
184
|
+
``` ruby
|
185
|
+
# dragon_steps.rb
|
186
|
+
steps_for :dragon do
|
187
|
+
use_steps :knight
|
188
|
+
|
189
|
+
attr_accessor :dragon
|
190
|
+
|
191
|
+
def dragon_attack
|
192
|
+
dragon * 10
|
193
|
+
end
|
194
|
+
|
195
|
+
step "there is a dragon" do
|
196
|
+
self.dragon = 1
|
197
|
+
end
|
198
|
+
|
199
|
+
step "the dragon attacks the knight" do
|
200
|
+
knight.attacked_for(dragon_attack)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# red_dragon_steps.rb
|
205
|
+
steps_for :red_dragon do
|
206
|
+
use_steps :dragon
|
207
|
+
|
208
|
+
attr_accessor :red_dragon
|
209
|
+
|
210
|
+
def dragon_attack
|
211
|
+
attack = super
|
212
|
+
if red_dragon
|
213
|
+
attack + 15
|
214
|
+
else
|
215
|
+
attack
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
step "the dragon breathes fire" do
|
220
|
+
self.red_dragon = 1
|
117
221
|
end
|
118
222
|
end
|
119
223
|
```
|
120
224
|
|
121
|
-
|
225
|
+
|
226
|
+
In this example we are making full use of Ruby's modules including using super
|
227
|
+
to call the included module's version of `dragon_attack`, for example with the
|
228
|
+
following feature file:
|
122
229
|
|
123
230
|
``` cucumber
|
124
|
-
|
125
|
-
|
231
|
+
Feature: Red Dragons are deadly
|
232
|
+
|
233
|
+
@dragon
|
234
|
+
Scenario:
|
235
|
+
Given there is a dragon
|
236
|
+
And there is a knight
|
237
|
+
When the dragon attacks the knight
|
238
|
+
Then the knight is alive
|
239
|
+
|
240
|
+
@red_dragon
|
241
|
+
Scenario:
|
242
|
+
Given there is a dragon
|
243
|
+
And the dragon breathes fire
|
244
|
+
And there is a knight
|
245
|
+
When the dragon attacks the knight
|
246
|
+
Then the knight is dead
|
126
247
|
```
|
127
248
|
|
128
|
-
|
129
|
-
|
249
|
+
### Auto-included steps
|
250
|
+
By default, Turnip will automatically make available any steps defined in
|
251
|
+
a `steps_for` block with the same name as the feature file being run. For
|
252
|
+
example, given this step file:
|
130
253
|
|
131
254
|
``` ruby
|
132
|
-
|
133
|
-
|
134
|
-
|
255
|
+
# user_signup_steps.rb
|
256
|
+
steps_for :user_signup do
|
257
|
+
step "I am on the homepage" do
|
258
|
+
...
|
259
|
+
end
|
260
|
+
|
261
|
+
step "I signup with valid info" do
|
262
|
+
...
|
263
|
+
end
|
264
|
+
|
265
|
+
step "I should see a welcome message" do
|
135
266
|
end
|
136
267
|
end
|
137
268
|
```
|
138
269
|
|
139
|
-
|
140
|
-
|
270
|
+
Then the following feature file would run just fine even though we
|
271
|
+
did not explicitly tag it with `@user_signup`.
|
141
272
|
|
142
|
-
|
273
|
+
``` cucumber
|
274
|
+
# user_signup.feature
|
275
|
+
Feature: A user can signup
|
276
|
+
Scenario: with email address
|
277
|
+
Given I am on the homepage
|
278
|
+
When I signup with valid info
|
279
|
+
Then I should see a welcome message
|
280
|
+
```
|
281
|
+
|
282
|
+
Note that the `steps_for :user_signup` did not technically have to
|
283
|
+
appear in the user_signup_steps.rb file; it could have been located
|
284
|
+
in any `steps.rb` file that was autoloaded by Turnip.
|
285
|
+
|
286
|
+
This feature can be turned off using the `Turnip::Config.autotag_features`
|
287
|
+
option if desired.
|
143
288
|
|
144
|
-
|
145
|
-
|
289
|
+
## Custom step placeholders
|
290
|
+
Do you want to be more specific in what to match in your step
|
291
|
+
placeholders? Do you find it bothersome to have to constantly cast them to the
|
292
|
+
correct type? Turnip supports custom placeholders to solve both problems, like this:
|
146
293
|
|
147
294
|
``` ruby
|
148
|
-
step "
|
149
|
-
|
295
|
+
step "there are :count monsters" do |count|
|
296
|
+
count.times { Monster.new(name) }
|
150
297
|
end
|
151
298
|
|
152
|
-
|
153
|
-
|
299
|
+
placeholder :count do
|
300
|
+
match /\d+/ do |count|
|
301
|
+
count.to_i
|
302
|
+
end
|
303
|
+
|
304
|
+
match /no/ do
|
305
|
+
0
|
306
|
+
end
|
154
307
|
end
|
155
308
|
```
|
156
309
|
|
157
|
-
|
158
|
-
run:
|
310
|
+
You would now be able to use these steps like this:
|
159
311
|
|
160
312
|
``` cucumber
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
@database
|
165
|
-
Scenario: do it through the database
|
313
|
+
Given there are 4 monsters
|
314
|
+
Given there are no monsters
|
166
315
|
```
|
167
316
|
|
168
|
-
|
317
|
+
Placeholders can extract matches from the regular expressions as well. For
|
318
|
+
example:
|
169
319
|
|
170
320
|
``` ruby
|
171
|
-
|
172
|
-
|
173
|
-
|
321
|
+
placeholder :monster do
|
322
|
+
match /(blue|green|red) (furry|bald) monster/ do |color, hair|
|
323
|
+
Monster.new(color, hair)
|
174
324
|
end
|
175
325
|
end
|
176
326
|
```
|
177
327
|
|
328
|
+
These regular expressions must not use anchors, e.g. `^` or `$`. They may not
|
329
|
+
contain named capture groups, e.g. `(?<color>blue|green)`.
|
330
|
+
|
178
331
|
## Using with Capybara
|
179
332
|
|
180
|
-
Just require `turnip/capybara
|
181
|
-
adding `-r turnip/capybara` to your `.rspec` file. You can now use the
|
333
|
+
Just require `turnip/capybara` in your `spec_helper`. You can now use the
|
182
334
|
same tags you'd use in Cucumber to switch between drivers e.g.
|
183
335
|
`@javascript` or `@selenium`. Your Turnip features will also be run
|
184
336
|
with the `:type => :request` metadata, so that Capybara is included and
|
data/Rakefile
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
steps_for :dragon do
|
2
|
+
use_steps :knight
|
3
|
+
|
4
|
+
attr_accessor :dragon
|
5
|
+
|
6
|
+
def dragon_attack
|
7
|
+
dragon * 10
|
8
|
+
end
|
9
|
+
|
10
|
+
step "there is a dragon" do
|
11
|
+
self.dragon = 1
|
12
|
+
end
|
13
|
+
|
14
|
+
step "the dragon attacks the knight" do
|
15
|
+
knight.attacked_for(dragon_attack)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
steps_for :knight do
|
2
|
+
attr_accessor :knight
|
3
|
+
|
4
|
+
class Knight
|
5
|
+
def initialize
|
6
|
+
@hp = 20
|
7
|
+
end
|
8
|
+
|
9
|
+
def alive?
|
10
|
+
@hp > 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def attacked_for(amount)
|
14
|
+
@hp -= amount
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
step "there is a knight" do
|
19
|
+
self.knight = Knight.new
|
20
|
+
end
|
21
|
+
|
22
|
+
step "the knight is alive" do
|
23
|
+
knight.should be_alive
|
24
|
+
end
|
25
|
+
|
26
|
+
step "the knight is dead" do
|
27
|
+
knight.should_not be_alive
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
steps_for :red_dragon do
|
2
|
+
use_steps :dragon
|
3
|
+
|
4
|
+
attr_accessor :red_dragon
|
5
|
+
|
6
|
+
def dragon_attack
|
7
|
+
attack = super
|
8
|
+
if red_dragon
|
9
|
+
attack + 15
|
10
|
+
else
|
11
|
+
attack
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
step "the dragon breathes fire" do
|
16
|
+
self.red_dragon = 1
|
17
|
+
end
|
18
|
+
end
|