turnip 0.3.1 → 1.0.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/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
|