turnip 0.3.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +128 -166
- data/examples/autoload_steps.feature +3 -2
- data/examples/errors.feature +8 -0
- data/examples/step_calling.feature +2 -0
- data/examples/steps/alignment_steps.rb +23 -0
- data/examples/{autoload_steps.rb → steps/autoload_steps.rb} +0 -0
- data/examples/steps/backtick_steps.rb +10 -0
- data/examples/steps/dragon_steps.rb +41 -0
- data/examples/{knight_steps.rb → steps/knight_steps.rb} +3 -1
- data/examples/{more_steps.rb → steps/more_steps.rb} +0 -0
- data/examples/{step_calling_steps.rb → steps/step_calling_steps.rb} +0 -0
- data/examples/{steps.rb → steps/steps.rb} +25 -1
- data/examples/steps_for.feature +2 -2
- data/examples/steps_with_variations.feature +17 -0
- data/lib/turnip.rb +19 -34
- data/lib/turnip/builder.rb +26 -24
- data/lib/turnip/define.rb +9 -0
- data/lib/turnip/dsl.rb +11 -17
- data/lib/turnip/execute.rb +15 -0
- data/lib/turnip/rspec.rb +80 -0
- data/lib/turnip/step_definition.rb +7 -32
- data/lib/turnip/version.rb +1 -1
- data/spec/builder_spec.rb +1 -40
- data/spec/define_and_execute.rb +38 -0
- data/spec/dsl_spec.rb +36 -19
- data/spec/integration_spec.rb +5 -1
- data/spec/spec_helper.rb +1 -3
- data/spec/step_definition_spec.rb +37 -51
- metadata +25 -55
- data/examples/alignment_steps.rb +0 -7
- data/examples/backtick_steps.rb +0 -4
- data/examples/dragon_steps.rb +0 -17
- data/examples/evil_steps.rb +0 -7
- data/examples/neutral_steps.rb +0 -7
- data/examples/red_dragon_steps.rb +0 -18
- data/lib/turnip/config.rb +0 -18
- data/lib/turnip/feature_file.rb +0 -20
- data/lib/turnip/loader.rb +0 -16
- data/lib/turnip/runner_dsl.rb +0 -9
- data/lib/turnip/scenario_context.rb +0 -41
- data/lib/turnip/scenario_runner.rb +0 -35
- data/lib/turnip/step_loader.rb +0 -27
- data/lib/turnip/step_module.rb +0 -89
- data/spec/feature_file_spec.rb +0 -18
- data/spec/runner_dsl_spec.rb +0 -23
- data/spec/scenario_context_spec.rb +0 -51
- data/spec/scenario_runner_spec.rb +0 -79
- data/spec/step_loader_spec.rb +0 -29
- data/spec/step_module_spec.rb +0 -106
data/README.md
CHANGED
@@ -27,7 +27,7 @@ Now edit the `.rspec` file in your project directory (create it if doesn't
|
|
27
27
|
exist), and add the following line:
|
28
28
|
|
29
29
|
```
|
30
|
-
-r turnip
|
30
|
+
-r turnip/rspec
|
31
31
|
```
|
32
32
|
|
33
33
|
## Development
|
@@ -73,33 +73,31 @@ Yes, that's really it.
|
|
73
73
|
|
74
74
|
## Defining steps
|
75
75
|
|
76
|
-
You
|
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:
|
76
|
+
You can define steps on any module:
|
82
77
|
|
83
78
|
``` ruby
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
79
|
+
module MonsterSteps
|
80
|
+
step "there is a monster" do
|
81
|
+
@monster = Monster.new
|
82
|
+
end
|
88
83
|
end
|
89
84
|
```
|
90
85
|
|
91
|
-
|
92
|
-
all `*steps.rb` files anywhere under that directory.
|
86
|
+
You can now include this module in RSpec:
|
93
87
|
|
94
|
-
|
95
|
-
|
88
|
+
``` ruby
|
89
|
+
RSpec.configure { |c| c.include MonsterSteps }
|
90
|
+
```
|
91
|
+
|
92
|
+
Steps are implemented as regular Ruby methods under the hood, so you can
|
93
|
+
use Ruby's normal inheritance chain to mix and match steps.
|
96
94
|
|
97
95
|
### 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
96
|
|
102
|
-
|
97
|
+
Turnip has a special module called `Turnip::Steps`, which is automatically
|
98
|
+
included in RSpec. If you add steps to this module, they are available in all
|
99
|
+
your features. As a convenience, there is a shortcut to doing this, just call
|
100
|
+
`step` in the global namespace like this:
|
103
101
|
|
104
102
|
``` ruby
|
105
103
|
step "there is a monster" do
|
@@ -107,6 +105,8 @@ step "there is a monster" do
|
|
107
105
|
end
|
108
106
|
```
|
109
107
|
|
108
|
+
### Placeholders
|
109
|
+
|
110
110
|
Note that unlike Cucumber, Turnip does not support regexps in step definitions.
|
111
111
|
You can however use placeholders in your step definitions, like this:
|
112
112
|
|
@@ -133,31 +133,40 @@ end
|
|
133
133
|
|
134
134
|
That will match both "there is X monster" or "there are X monsters".
|
135
135
|
|
136
|
-
You can also define custom step placeholders.
|
136
|
+
You can also define custom step placeholders. More on that later.
|
137
137
|
|
138
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.
|
142
139
|
|
143
|
-
|
140
|
+
Since steps are defined on modules, you can pick and choose which of them are
|
141
|
+
available in which feature. This can be extremely useful if you have a large
|
142
|
+
number of steps, and do not want them to potentially conflict.
|
143
|
+
|
144
|
+
If you had some scenarios which talk to the database directly, and some which
|
145
|
+
go through a user interface, you could implement it as follows:
|
144
146
|
|
145
147
|
``` ruby
|
146
|
-
|
148
|
+
module InterfaceSteps
|
147
149
|
step "I do it" do
|
148
150
|
...
|
149
151
|
end
|
150
152
|
end
|
151
153
|
|
152
|
-
|
154
|
+
module DatabaseSteps
|
153
155
|
step "I do it" do
|
154
156
|
...
|
155
157
|
end
|
156
158
|
end
|
159
|
+
|
160
|
+
RSpec.configure do |config|
|
161
|
+
config.include InterfaceSteps, :interface => true
|
162
|
+
config.include DatabaseSteps, :database => true
|
163
|
+
end
|
157
164
|
```
|
158
165
|
|
159
|
-
|
160
|
-
|
166
|
+
Turnip turns tags into RSpec metadata, so you can use RSpec's conditional
|
167
|
+
include feature to include these steps only for those scenarios tagged the
|
168
|
+
appropriate way. So even though the step is named the same, you can now use it
|
169
|
+
in your feature files like so:
|
161
170
|
|
162
171
|
``` cucumber
|
163
172
|
@interface
|
@@ -167,149 +176,103 @@ Scenario: do it through the interface
|
|
167
176
|
Scenario: do it through the database
|
168
177
|
```
|
169
178
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
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
|
+
Be careful though not to tag a feature with both `@interface` and `@database`
|
180
|
+
in this example. Since steps use the Ruby inheritance chain, the step which is
|
181
|
+
included last will "win", just like any other Ruby method. This might not be
|
182
|
+
what you expect.
|
179
183
|
|
180
|
-
|
181
|
-
|
182
|
-
defined in another `steps_for` block. The syntax for that is `use_steps`:
|
184
|
+
Since this pattern of creating a module and including it for a specific tag
|
185
|
+
is very common, we have created a handy shortcut for it:
|
183
186
|
|
184
187
|
``` ruby
|
185
|
-
|
186
|
-
|
187
|
-
|
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)
|
188
|
+
steps_for :interface do
|
189
|
+
step "I do it" do
|
190
|
+
...
|
201
191
|
end
|
202
192
|
end
|
193
|
+
```
|
203
194
|
|
204
|
-
|
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
|
195
|
+
Check out [features/alignment_steps.rb](https://github.com/jnicklas/turnip/blob/master/examples/steps/alignment_steps.rb)
|
218
196
|
|
219
|
-
|
220
|
-
self.red_dragon = 1
|
221
|
-
end
|
222
|
-
end
|
223
|
-
```
|
197
|
+
for an example.
|
224
198
|
|
199
|
+
### Where to place steps
|
225
200
|
|
226
|
-
|
227
|
-
|
228
|
-
|
201
|
+
Turnip automatically loads your `spec_helper` file. From there you can place
|
202
|
+
your steps wherever you want, and load them however you like. For example, if
|
203
|
+
you were to put your steps in `spec/steps`, you could load them like this:
|
229
204
|
|
230
|
-
```
|
231
|
-
|
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
|
205
|
+
``` ruby
|
206
|
+
Dir.glob("spec/steps/**/*steps.rb") { |f| load f, true }
|
247
207
|
```
|
248
208
|
|
249
209
|
### Calling steps from other steps
|
250
|
-
|
251
|
-
|
210
|
+
|
211
|
+
Since steps are Ruby methods you can call them like other Ruby methods.
|
212
|
+
However, since the step description likely contains spaces and other special
|
213
|
+
characters, you will probably have to use `send` to call the step:
|
252
214
|
|
253
215
|
``` ruby
|
254
|
-
step "
|
255
|
-
@value =
|
216
|
+
step "the value is :num" do |num|
|
217
|
+
@value = num
|
256
218
|
end
|
257
219
|
|
258
|
-
step "
|
259
|
-
|
260
|
-
@value += 1
|
220
|
+
step "the value is twice as much as :num" do |num|
|
221
|
+
send "the value is :num", num * 2
|
261
222
|
end
|
262
223
|
```
|
263
224
|
|
264
|
-
|
265
|
-
|
266
|
-
`a random step`. You can think of it as a simple method call.
|
225
|
+
If you use the second step, it will call into the first step, sending in the
|
226
|
+
doubled value.
|
267
227
|
|
268
|
-
|
269
|
-
|
270
|
-
a `steps_for` block with the same name as the feature file being run. For
|
271
|
-
example, given this step file:
|
228
|
+
Sometimes you will want to call the step just like you would from your feature
|
229
|
+
file, in that case you can use the `step` method:
|
272
230
|
|
273
231
|
``` ruby
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
...
|
278
|
-
end
|
279
|
-
|
280
|
-
step "I signup with valid info" do
|
281
|
-
...
|
282
|
-
end
|
232
|
+
step "the value is :num" do |num|
|
233
|
+
@value = num
|
234
|
+
end
|
283
235
|
|
284
|
-
|
285
|
-
|
286
|
-
end
|
236
|
+
step "the value is the magic number"
|
237
|
+
step "the value is 3"
|
287
238
|
end
|
288
239
|
```
|
289
240
|
|
290
|
-
|
291
|
-
did not explicitly tag it with `@user_signup`.
|
241
|
+
### Calling steps manually
|
292
242
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
243
|
+
This is a more esoteric feature of Turnip, of use mostly to people who want to
|
244
|
+
do crazy stuff. You can use `send` to call any Turnip step, no matter where it
|
245
|
+
is defined or included. Additionally, the `Turnip::Execute` module has a method
|
246
|
+
called `step`, this method executes a step, given a string as it might appear
|
247
|
+
in a feature file. This is the same `step` method you used above to call steps
|
248
|
+
from within other steps.
|
249
|
+
|
250
|
+
For example:
|
251
|
+
|
252
|
+
``` ruby
|
253
|
+
class Monster
|
254
|
+
include Turnip::Execute
|
301
255
|
|
302
|
-
|
303
|
-
|
304
|
-
|
256
|
+
step("sing a song") { "Arrrghghggh" }
|
257
|
+
step("eat :count villager(s)") { Villager.eat(count) }
|
258
|
+
end
|
259
|
+
|
260
|
+
monster = Monster.new
|
261
|
+
monster.step("sing a song")
|
262
|
+
monster.step("eat 1 villager")
|
263
|
+
monster.step("eat 5 villagers")
|
264
|
+
monster.send("eat :count villager(s)", 5)
|
265
|
+
```
|
305
266
|
|
306
|
-
|
307
|
-
|
267
|
+
Note that in this case `step` from `Turnip::Execute` is an *instance* method,
|
268
|
+
whereas `step` used to define the step is a *class* method, they are *not* the
|
269
|
+
same method.
|
308
270
|
|
309
271
|
## Custom step placeholders
|
310
|
-
|
311
|
-
|
312
|
-
|
272
|
+
|
273
|
+
Do you want to be more specific in what to match in your step placeholders? Do
|
274
|
+
you find it bothersome to have to constantly cast them to the correct type?
|
275
|
+
Turnip supports custom placeholders to solve both problems, like this:
|
313
276
|
|
314
277
|
``` ruby
|
315
278
|
step "there are :count monsters" do |count|
|
@@ -349,6 +312,7 @@ These regular expressions must not use anchors, e.g. `^` or `$`. They may not
|
|
349
312
|
contain named capture groups, e.g. `(?<color>blue|green)`.
|
350
313
|
|
351
314
|
## Table Steps
|
315
|
+
|
352
316
|
Turnip also supports steps that take a table as a parameter similar to Cucumber:
|
353
317
|
|
354
318
|
``` cucumber
|
@@ -361,7 +325,7 @@ Scenario: This is a feature with a table
|
|
361
325
|
And "Moorg" should have 12 hitpoints
|
362
326
|
```
|
363
327
|
The table is a `Turnip::Table` object which works in much the same way as Cucumber's
|
364
|
-
`Cucumber::Ast::Table`
|
328
|
+
`Cucumber::Ast::Table` objects.
|
365
329
|
|
366
330
|
E.g. converting the `Turnip::Table` to an array of hashes:
|
367
331
|
|
@@ -376,34 +340,32 @@ end
|
|
376
340
|
|
377
341
|
## Using with Capybara
|
378
342
|
|
379
|
-
Just require `turnip/capybara` in your `spec_helper`. You can now use the
|
380
|
-
|
381
|
-
`@
|
382
|
-
|
383
|
-
|
343
|
+
Just require `turnip/capybara` in your `spec_helper`. You can now use the same
|
344
|
+
tags you'd use in Cucumber to switch between drivers e.g. `@javascript` or
|
345
|
+
`@selenium`. Your Turnip features will also be run with the `:type => :request`
|
346
|
+
metadata, so that Capybara is included and also any other extensions you might
|
347
|
+
want to add.
|
384
348
|
|
385
349
|
## License
|
386
350
|
|
387
351
|
(The MIT License)
|
388
352
|
|
389
|
-
Copyright (c) 2011 Jonas Nicklas
|
390
|
-
|
391
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
IN
|
406
|
-
|
407
|
-
|
408
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
409
|
-
|
353
|
+
Copyright (c) 2011-2012 Jonas Nicklas
|
354
|
+
|
355
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
356
|
+
this software and associated documentation files (the 'Software'), to deal in
|
357
|
+
the Software without restriction, including without limitation the rights to
|
358
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
359
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
360
|
+
so, subject to the following conditions:
|
361
|
+
|
362
|
+
The above copyright notice and this permission notice shall be included in all
|
363
|
+
copies or substantial portions of the Software.
|
364
|
+
|
365
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
366
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
367
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
368
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
369
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
370
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
371
|
+
SOFTWARE.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Alignment
|
2
|
+
attr_accessor :alignment
|
3
|
+
|
4
|
+
step "that alignment should be :alignment" do |expected_alignment|
|
5
|
+
alignment.should eq(expected_alignment)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
steps_for :evil do
|
10
|
+
include Alignment
|
11
|
+
|
12
|
+
step "the monster has an alignment" do
|
13
|
+
self.alignment = 'Evil'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
steps_for :neutral do
|
18
|
+
include Alignment
|
19
|
+
|
20
|
+
step "the monster has an alignment" do
|
21
|
+
self.alignment = 'Neutral'
|
22
|
+
end
|
23
|
+
end
|