spinach 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/Gemfile +1 -1
- data/README.markdown +158 -112
- data/bin/spinach +0 -1
- data/features/before_and_after_hooks.feature +10 -0
- data/features/before_and_after_hooks_inheritance.feature +12 -0
- data/features/reporting/customized_reporter.feature +9 -0
- data/features/reporting/show_step_source_location.feature +1 -1
- data/features/steps/before_and_after_hooks.rb +21 -0
- data/features/steps/before_and_after_hooks_inheritance.rb +43 -0
- data/features/steps/reporting/use_customized_reporter.rb +98 -0
- data/lib/spinach.rb +1 -0
- data/lib/spinach/cli.rb +12 -20
- data/lib/spinach/config.rb +22 -1
- data/lib/spinach/dsl.rb +82 -0
- data/lib/spinach/feature_steps.rb +3 -0
- data/lib/spinach/helpers.rb +16 -0
- data/lib/spinach/runner.rb +9 -0
- data/lib/spinach/runner/feature_runner.rb +1 -1
- data/lib/spinach/runner/scenario_runner.rb +2 -0
- data/lib/spinach/tags_matcher.rb +15 -8
- data/lib/spinach/version.rb +1 -1
- data/spinach.gemspec +2 -2
- data/test/spinach/cli_test.rb +70 -18
- data/test/spinach/config_test.rb +74 -63
- data/test/spinach/dsl_test.rb +108 -0
- data/test/spinach/feature_steps_test.rb +8 -0
- data/test/spinach/helpers_test.rb +9 -0
- data/test/spinach/runner/feature_runner_test.rb +19 -1
- data/test/spinach/runner/scenario_runner_test.rb +28 -5
- data/test/spinach/runner_test.rb +41 -0
- metadata +101 -31
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -22,117 +22,130 @@ would be irresponsible :)
|
|
22
22
|
|
23
23
|
Start by adding spinach to your Gemfile:
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
```ruby
|
26
|
+
group :test do
|
27
|
+
gem 'spinach'
|
28
|
+
# gem 'rspec'
|
29
|
+
end
|
30
|
+
```
|
29
31
|
|
30
32
|
Spinach works out-of-the-box with your favorite test suite, but you can also
|
31
33
|
use it with RSpec as well if you put the following in `features/support/env.rb`:
|
32
34
|
|
33
|
-
|
35
|
+
```ruby
|
36
|
+
require 'rspec'
|
37
|
+
```
|
34
38
|
|
35
39
|
Now create a `features` folder in your app or library and write your first
|
36
40
|
feature:
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
42
|
+
```cucumber
|
43
|
+
Feature: Test how spinach works
|
44
|
+
In order to know what the heck is spinach
|
45
|
+
As a developer
|
46
|
+
I want it to behave in an expected way
|
47
|
+
|
48
|
+
Scenario: Formal greeting
|
49
|
+
Given I have an empty array
|
50
|
+
And I append my first name and my last name to it
|
51
|
+
When I pass it to my super-duper method
|
52
|
+
Then the output should contain a formal greeting
|
53
|
+
|
54
|
+
Scenario: Informal greeting
|
55
|
+
Given I have an empty array
|
56
|
+
And I append only my first name to it
|
57
|
+
When I pass it to my super-duper method
|
58
|
+
Then the output should contain a casual greeting
|
59
|
+
```
|
54
60
|
|
55
61
|
Now for the steps file. Remember that in Spinach steps are just Ruby classes,
|
56
62
|
following a camelcase naming convention. Spinach generator will do some
|
57
63
|
scaffolding for you:
|
58
64
|
|
59
|
-
|
65
|
+
```shell
|
66
|
+
$ spinach --generate
|
67
|
+
```
|
60
68
|
|
61
69
|
Spinach will detect your features and generate the following class:
|
62
70
|
|
63
71
|
## features/steps/test_how_spinach_works.rb
|
64
72
|
|
65
|
-
|
66
|
-
|
67
|
-
|
73
|
+
```ruby
|
74
|
+
class TestHowSpinachWorks < Spinach::FeatureSteps
|
75
|
+
Given 'I have an empty array' do
|
76
|
+
end
|
68
77
|
|
69
|
-
|
70
|
-
|
78
|
+
And 'I append my first name and my last name to it' do
|
79
|
+
end
|
71
80
|
|
72
|
-
|
73
|
-
|
81
|
+
When 'I pass it to my super-duper method' do
|
82
|
+
end
|
74
83
|
|
75
|
-
|
76
|
-
|
84
|
+
Then 'the output should contain a formal greeting' do
|
85
|
+
end
|
77
86
|
|
78
|
-
|
79
|
-
|
87
|
+
And 'I append only my first name to it' do
|
88
|
+
end
|
80
89
|
|
81
|
-
|
82
|
-
|
83
|
-
|
90
|
+
Then 'the output should contain a casual greeting' do
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
84
94
|
|
85
95
|
Then, you can fill it in with your logic - remember, it's just a class, you can
|
86
96
|
use private methods, mix in modules or whatever!
|
87
97
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
Then 'the output should contain a formal greeting' do
|
104
|
-
@output.must_include "Hello, mr. John Doe"
|
105
|
-
end
|
106
|
-
|
107
|
-
And 'I append only my first name to it' do
|
108
|
-
@array += ["John"]
|
109
|
-
end
|
110
|
-
|
111
|
-
Then 'the output should contain a casual greeting' do
|
112
|
-
@output.must_include "Yo, John! Whassup?"
|
113
|
-
end
|
114
|
-
|
115
|
-
private
|
116
|
-
|
117
|
-
def capture_output
|
118
|
-
out = StringIO.new
|
119
|
-
$stdout = out
|
120
|
-
$stderr = out
|
121
|
-
yield
|
122
|
-
$stdout = STDOUT
|
123
|
-
$stderr = STDERR
|
124
|
-
out.string
|
125
|
-
end
|
98
|
+
```ruby
|
99
|
+
class TestHowSpinachWorks < Spinach::FeatureSteps
|
100
|
+
Given 'I have an empty array' do
|
101
|
+
@array = Array.new
|
102
|
+
end
|
103
|
+
|
104
|
+
And 'I append my first name and my last name to it' do
|
105
|
+
@array += ["John", "Doe"]
|
106
|
+
end
|
107
|
+
|
108
|
+
When 'I pass it to my super-duper method' do
|
109
|
+
@output = capture_output do
|
110
|
+
Greeter.greet(@array)
|
126
111
|
end
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
112
|
+
end
|
113
|
+
|
114
|
+
Then 'the output should contain a formal greeting' do
|
115
|
+
@output.must_include "Hello, mr. John Doe"
|
116
|
+
end
|
117
|
+
|
118
|
+
And 'I append only my first name to it' do
|
119
|
+
@array += ["John"]
|
120
|
+
end
|
121
|
+
|
122
|
+
Then 'the output should contain a casual greeting' do
|
123
|
+
@output.must_include "Yo, John! Whassup?"
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def capture_output
|
129
|
+
out = StringIO.new
|
130
|
+
$stdout = out
|
131
|
+
$stderr = out
|
132
|
+
yield
|
133
|
+
$stdout = STDOUT
|
134
|
+
$stderr = STDERR
|
135
|
+
out.string
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
module Greeter
|
140
|
+
def self.greet(name)
|
141
|
+
if name.length > 1
|
142
|
+
puts "Hello, mr. #{name.join(' ')}"
|
143
|
+
else
|
144
|
+
puts "Yo, #{name.first}! Whassup?"
|
135
145
|
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
```
|
136
149
|
|
137
150
|
Then run your feature again running `spinach` and watch it all turn green! :)
|
138
151
|
|
@@ -143,47 +156,59 @@ used for different purposes:
|
|
143
156
|
|
144
157
|
- applying some actions using hooks (eg: `@javascript`, `@transaction`, `@vcr`)
|
145
158
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
159
|
+
```ruby
|
160
|
+
# When using Capybara, you can switch the driver to use another one with
|
161
|
+
# javascript capabilities (Selenium, Poltergeist, capybara-webkit, ...)
|
162
|
+
#
|
163
|
+
# Spinach already integrates with Capybara if you add
|
164
|
+
# `require spinach/capybara` in `features/support/env.rb`.
|
165
|
+
#
|
166
|
+
# This example is extracted from this integration.
|
167
|
+
Spinach.hooks.on_tag("javascript") do
|
168
|
+
::Capybara.current_driver = ::Capybara.javascript_driver
|
169
|
+
end
|
170
|
+
```
|
156
171
|
|
157
172
|
- filtering (eg: `@module-a`, `@customer`, `@admin`, `@bug-12`, `@feat-1`)
|
158
173
|
|
159
|
-
|
174
|
+
```cucumber
|
175
|
+
# Given a feature file with this content
|
160
176
|
|
161
|
-
|
162
|
-
|
177
|
+
@feat-1
|
178
|
+
Feature: So something great
|
163
179
|
|
164
|
-
|
180
|
+
Scenario: Make it possible
|
165
181
|
|
166
|
-
|
167
|
-
|
182
|
+
@bug-12
|
183
|
+
Scenario: Ensure no regression on this
|
184
|
+
```
|
168
185
|
|
169
186
|
Then you can run all Scenarios in your suite related to `@feat-1` using:
|
170
187
|
|
171
|
-
|
188
|
+
```shell
|
189
|
+
$ spinach --tags @feat-1
|
190
|
+
```
|
172
191
|
|
173
192
|
Or only Scenarios related to `@feat-1` and `@bug-12` using:
|
174
193
|
|
175
|
-
|
194
|
+
```shell
|
195
|
+
$ spinach --tags @feat-1,@bug-12
|
196
|
+
```
|
176
197
|
|
177
198
|
Or only Scenarios related to `@feat-1` excluding `@bug-12` using:
|
178
199
|
|
179
|
-
|
200
|
+
```shell
|
201
|
+
$ spinach --tags @feat-1,~@bug-12
|
202
|
+
```
|
180
203
|
|
181
204
|
By default Spinach will ignore Scenarios marked with the tag `@wip` or whose
|
182
205
|
Feature is marked with the tag `@wip`. Those are meant to be work in progress,
|
183
206
|
scenarios that are pending while you work on them. To explicitly run those, use
|
184
207
|
the `--tags` option:
|
185
208
|
|
186
|
-
|
209
|
+
```shell
|
210
|
+
$ spinach --tags @wip
|
211
|
+
```
|
187
212
|
|
188
213
|
## Hook architecture
|
189
214
|
|
@@ -192,22 +217,44 @@ after any feature, scenario or step execution.
|
|
192
217
|
|
193
218
|
So, for example, you could:
|
194
219
|
|
195
|
-
|
196
|
-
|
197
|
-
|
220
|
+
```ruby
|
221
|
+
Spinach.hooks.before_scenario do |scenario|
|
222
|
+
clear_database
|
223
|
+
end
|
198
224
|
|
199
|
-
|
200
|
-
|
201
|
-
|
225
|
+
Spinach.hooks.on_successful_step do |step, location|
|
226
|
+
count_steps(step.scenario.steps)
|
227
|
+
end
|
202
228
|
|
203
|
-
|
204
|
-
|
205
|
-
|
229
|
+
Spinach.hooks.after_run do |status|
|
230
|
+
send_mail if status == 0
|
231
|
+
end
|
232
|
+
```
|
206
233
|
|
207
234
|
Full hook documentation is here:
|
208
235
|
|
209
236
|
[Spinach's hook documentation on rubydoc](http://rubydoc.info/github/codegram/spinach/master/Spinach/Hooks)
|
210
237
|
|
238
|
+
## Local Before and After Hooks
|
239
|
+
|
240
|
+
Sometimes it feels awkward to add steps into feature file just because you need to do some test setup and cleanup. And it is equally awkward to add a global hooks for this purpose. For example, if you want to add a session timeout feature, to do so, you want to set the session timeout time to 1 second just for this feature, and put the normal timeout back after this feature. It doesn't make sense to add two steps in the feature file just to change the session timeout value. In this scenario, a ```before``` and ```after``` blocks are perfect for this kind of tasks. Below is an example implementation:
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
class SessionTimeout < Spinach::FeatureSteps
|
244
|
+
attr_accessor :original_timeout_value
|
245
|
+
before do
|
246
|
+
self.original_timeout_value = session_timeout_value
|
247
|
+
change_session_timeout_to 1.second
|
248
|
+
end
|
249
|
+
|
250
|
+
after do
|
251
|
+
change_session_timeout_to original_timeout_value
|
252
|
+
end
|
253
|
+
|
254
|
+
# remaining steps
|
255
|
+
end
|
256
|
+
```
|
257
|
+
|
211
258
|
## Wanna use it with Rails 3?
|
212
259
|
|
213
260
|
Use [spinach-rails](http://github.com/codegram/spinach-rails)
|
@@ -258,4 +305,3 @@ You can easily contribute to Spinach. Its codebase is simple and
|
|
258
305
|
## License
|
259
306
|
|
260
307
|
MIT License. Copyright 2011 [Codegram Technologies](http://codegram.com)
|
261
|
-
|
data/bin/spinach
CHANGED
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: Before and after hooks
|
2
|
+
In order to setup and clean the test environment for a single feature
|
3
|
+
As a developer
|
4
|
+
I want to be able to use before and after hooks within the step class
|
5
|
+
|
6
|
+
Scenario: Happy path
|
7
|
+
Then I can verify the variable setup in the before hook
|
8
|
+
|
9
|
+
Scenario: Inter-dependency - after hook cleans up (after happy path)
|
10
|
+
Then I can verify the variable setup in the before hook
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Feature: Before and after hooks inheritance
|
2
|
+
In order to maintain the super classes' before and after code
|
3
|
+
As a developer
|
4
|
+
I want to chain the before and after blocks
|
5
|
+
|
6
|
+
Scenario: Happy path
|
7
|
+
Then I can see the variable setup in the super class before hook
|
8
|
+
And I can see the variable being overridden in the subclass
|
9
|
+
|
10
|
+
Scenario: Inter-dependency - after hook cleans up (after happy path)
|
11
|
+
Then I can see the variable setup in the super class before hook
|
12
|
+
And I can see the variable being overridden in the subclass
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Feature: Use customized reporter
|
2
|
+
As a developer
|
3
|
+
I want to use a different reporter
|
4
|
+
So I can have different output
|
5
|
+
|
6
|
+
Scenario: Happy path
|
7
|
+
Given I have a feature that has no error or failure
|
8
|
+
When I run it using the new reporter
|
9
|
+
Then I see the desired output
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Feature: Show step source location
|
2
2
|
As a developer
|
3
3
|
I want spinach to give me every step source location in output
|
4
|
-
So I can
|
4
|
+
So I can easily know where I defined a step
|
5
5
|
|
6
6
|
Scenario: Show class steps source location in output when all is ok
|
7
7
|
Given I have a feature that has no error or failure
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class BeforeAndAfterHooks < Spinach::FeatureSteps
|
2
|
+
class << self
|
3
|
+
attr_accessor :var1
|
4
|
+
end
|
5
|
+
|
6
|
+
before do
|
7
|
+
if self.class.var1.nil?
|
8
|
+
self.class.var1 = :clean
|
9
|
+
else
|
10
|
+
self.class.var1 = :dirty
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
after do
|
15
|
+
self.class.var1 = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
Then 'I can verify the variable setup in the before hook' do
|
19
|
+
self.class.var1.must_equal :clean
|
20
|
+
end
|
21
|
+
end
|