cukesalad 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Examples/Calculator/Gemfile +4 -0
- data/Examples/Calculator/Gemfile.lock +36 -20
- data/Examples/Calculator/README.md +7 -0
- data/Examples/Calculator/cucumber.yml +1 -1
- data/Examples/Calculator/features/addition.feature +1 -1
- data/Examples/Calculator/features/lib/{alternative → browser}/roles/calculating_web_user.rb +1 -1
- data/Examples/Calculator/features/lib/default/roles/calculating_individual.rb +3 -5
- data/Examples/Calculator/features/lib/default/tasks/reference/calculations.rb +4 -2
- data/Examples/Calculator/features/subtraction.feature +1 -1
- data/Examples/Calculator/lib/calculator_operations.rb +4 -0
- data/Examples/Calculator/lib/web_calculator.rb +17 -20
- data/Examples/Calculator/views/index.erb +7 -0
- data/Examples/Calculator/views/layout.erb +2 -0
- data/Gemfile.lock +12 -12
- data/VERSION +1 -1
- data/cukesalad.gemspec +2 -2
- data/features/README.markdown +39 -273
- data/features/a_simple_tutorial/README.markdown +277 -0
- data/features/creating_a_new_project.feature +24 -22
- data/features/defining_roles/README.md +29 -0
- data/features/{define_a_role.feature → defining_roles/define_a_role.feature} +8 -0
- data/features/{prepare_the_actor_for_the_role.feature → defining_roles/prepare_the_actor_for_the_role.feature} +0 -0
- data/features/defining_tasks/README.md +12 -0
- data/features/{define_a_task.feature → defining_tasks/define_a_task.feature} +58 -17
- data/features/{define_a_task_with_arguments.feature → defining_tasks/define_a_task_with_arguments.feature} +0 -0
- data/features/{remember_information_between_steps.feature → defining_tasks/remember_information_between_steps.feature} +0 -0
- data/features/expectations/README.md +21 -0
- data/features/{expressing_expectations.feature → expectations/expressing_expectations.feature} +0 -0
- data/features/lib/tasks/interactively_run.rb +5 -0
- data/features/lib/tasks/see_the_step_has.rb +1 -1
- data/lib/cukesalad/actor.rb +5 -5
- data/lib/cukesalad/cli.rb +1 -1
- data/lib/cukesalad/cucumber_steps.rb +3 -7
- data/lib/cukesalad/specifics.rb +3 -3
- data/lib/cukesalad/version.rb +1 -1
- data/spec/cukesalad/cli_spec.rb +1 -1
- metadata +35 -24
@@ -0,0 +1,277 @@
|
|
1
|
+
## Goals->Tasks->Actions
|
2
|
+
The terms *"actions"* and *"tasks"* come from Task Analysis, as used in User Centred Design (UCD) of Human Computer Interfaces (HCI) a.k.a. User Experience (UX):
|
3
|
+
|
4
|
+
* *Goal:* What we’re trying to achieve which has one or more…
|
5
|
+
* *Tasks:* The high-level work-item that we complete to fulfil the goal, each having one or more…
|
6
|
+
* *Actions:* The specific steps or interactions we execute to complete the task.
|
7
|
+
|
8
|
+
More information about Goals, Tasks and Actions can be found in this [blog post](http://antonymarcano.com/blog/2011/03/goals-tasks-action/)
|
9
|
+
|
10
|
+
Let's see how this works with a simple example...
|
11
|
+
|
12
|
+
## Install
|
13
|
+
|
14
|
+
gem install cukesalad
|
15
|
+
|
16
|
+
## Let's Get started
|
17
|
+
|
18
|
+
Create a new project called Calculator:
|
19
|
+
|
20
|
+
cukesalad Calculator
|
21
|
+
|
22
|
+
Or, if you have an existing cucumber project that you want to configure to use cukesalad, you can type:
|
23
|
+
|
24
|
+
cukesalad
|
25
|
+
|
26
|
+
## Write Features
|
27
|
+
|
28
|
+
In `features/`, let's create our first feature file - `a_place_to_start.feature`:
|
29
|
+
|
30
|
+
Feature: A Place To Start
|
31
|
+
As Callie, a calculating individual
|
32
|
+
I want to know when my calculator is on
|
33
|
+
So that I know when I can start calculating
|
34
|
+
|
35
|
+
Scenario: Let's Begin
|
36
|
+
Given I am a Calculating Individual
|
37
|
+
When I attempt to switch on the calculator
|
38
|
+
Then I should see the answer '0'
|
39
|
+
|
40
|
+
Let's take a moment to understand this scenario:
|
41
|
+
|
42
|
+
Scenario: Let's Begin
|
43
|
+
Given I am a <some role>
|
44
|
+
When I attempt to <do some task>
|
45
|
+
Then I should <ask some question> '<expected answer>'
|
46
|
+
|
47
|
+
To get this working, we don't need to write any steps. Instead, we describe tasks...
|
48
|
+
|
49
|
+
## Create Tasks
|
50
|
+
|
51
|
+
Explaining how to do a _task_ is easy:
|
52
|
+
Create a new file, `features/lib/tasks/switch_on_the_calculator.rb`
|
53
|
+
|
54
|
+
Remember the step `When I attempt to switch on the calculator`
|
55
|
+
|
56
|
+
in_order_to "switch on the calculator" do
|
57
|
+
switch_on_the_calculator
|
58
|
+
end
|
59
|
+
|
60
|
+
Remember the step `Then I should see the answer '0'`
|
61
|
+
Now we need `task/see_the_answer.rb`
|
62
|
+
|
63
|
+
in_order_to "see the answer" do
|
64
|
+
look_at_the_display
|
65
|
+
end
|
66
|
+
|
67
|
+
Now we've explained the _tasks_, we need to define the _role_ that performs them. In
|
68
|
+
this example, we need to explain how the `CalculatingIndividual` _role_ works...
|
69
|
+
|
70
|
+
## Create Roles
|
71
|
+
|
72
|
+
Remember the step `Given I am a Calculating Individual`?
|
73
|
+
|
74
|
+
We explain a _role_ by creating a new file
|
75
|
+
called `features/lib/roles/calculating_individual.rb`
|
76
|
+
|
77
|
+
module CalculatingIndividual
|
78
|
+
|
79
|
+
def switch_on_the_calculator
|
80
|
+
@calculator = Calculator.new
|
81
|
+
end
|
82
|
+
|
83
|
+
def look_at_the_display
|
84
|
+
@calculator.display
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
You'll need a class called Calculator on the load path of course, but that's it.
|
89
|
+
|
90
|
+
From your project folder, run (_note: '%' is our command prompt_)
|
91
|
+
|
92
|
+
% cucumber
|
93
|
+
|
94
|
+
We now have our first passing Feature, without creating a single step definition!
|
95
|
+
|
96
|
+
## Wash, rinse, repeat
|
97
|
+
|
98
|
+
Let's try another scenario...
|
99
|
+
|
100
|
+
Scenario Outline: Find the sum of two numbers
|
101
|
+
Given I am a Calculating Individual
|
102
|
+
And I was able to switch on the calculator
|
103
|
+
When I attempt to add: the number '10' and the number '10'
|
104
|
+
Then I should see the answer '20'
|
105
|
+
|
106
|
+
Notice that we've reused 'switch on the calculator'.
|
107
|
+
|
108
|
+
The new _When_ step has a slightly different layout.
|
109
|
+
Let's examine that for a second... See below. Notice the ':' (colon) after `<do something>` followed by the name-value pairs:
|
110
|
+
|
111
|
+
When I attempt to <do something>: <name> '<value>' <name> '<value>'
|
112
|
+
|
113
|
+
You can also use a ',' (comma) in situations where a colon wouldn't quite work:
|
114
|
+
|
115
|
+
When I attempt to <do something>, <name> '<value>' <name> '<value>'
|
116
|
+
|
117
|
+
`<do something>` can be as many words as you like. You can have as many name-value pairs as you like.
|
118
|
+
|
119
|
+
For this to work we need a task called `tasks/add.rb` that explains the individual actions required to complete the task:
|
120
|
+
|
121
|
+
in_order_to "add" do
|
122
|
+
enter @value_of(:the_number)
|
123
|
+
press :plus
|
124
|
+
enter @value_of(:and_the_number)
|
125
|
+
press :equals
|
126
|
+
end
|
127
|
+
|
128
|
+
Notice how the `value_of` lines use symbols that correspond to the wording `'the number '10' to the number '10'` in the "When" step.
|
129
|
+
|
130
|
+
There is some 'syntactic sugar' that we can use to dress this up a little and make it read nicer... a simple attribute mapping (using Ruby 1.9.x syntax):
|
131
|
+
|
132
|
+
in_order_to "add", the_number: :first_number, to_the_number: :second_number do
|
133
|
+
enter the :first_number
|
134
|
+
press :plus
|
135
|
+
enter the :second_number
|
136
|
+
press :equals
|
137
|
+
end
|
138
|
+
|
139
|
+
All we've done is mapped `:the_number` to `:first_number` and `:to_the_number` to `:second_number`. There is a special method called "the" that allows you to reference the mapped values rather than the symbols derived from the scenario.
|
140
|
+
|
141
|
+
Now all we need to do is create the corresponding methods in `calculating_individual.rb`.
|
142
|
+
|
143
|
+
module CalculatingIndividual
|
144
|
+
|
145
|
+
def switch_on_the_calculator
|
146
|
+
@calculator = Calculator.new
|
147
|
+
@operate_with = {
|
148
|
+
plus: :+,
|
149
|
+
minus: :-
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
def enter value
|
154
|
+
@calculator.enter value.to_i
|
155
|
+
end
|
156
|
+
|
157
|
+
def press next_operator
|
158
|
+
if next_operator == :equals
|
159
|
+
equals
|
160
|
+
else
|
161
|
+
@calculator.get_ready_to @operate_with[next_operator]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def equals
|
166
|
+
@calculator.equals
|
167
|
+
end
|
168
|
+
|
169
|
+
def look_at_the_display
|
170
|
+
@calculator.display
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
Of course you'll have to [implement the calculator too](https://github.com/RiverGlide/CukeSalad/blob/master/Examples/Calculator/lib/calculator.rb)
|
175
|
+
|
176
|
+
Now, you can run cucumber again:
|
177
|
+
|
178
|
+
% cucumber
|
179
|
+
|
180
|
+
There's no need to write `step_definitions`...
|
181
|
+
Simply express the _roles_ and the _tasks_ in clear,
|
182
|
+
concise, easy to read classes.
|
183
|
+
|
184
|
+
If we want to know what things we can say, instead of trawling through a step-def ruby file, we can look in our tasks folder:
|
185
|
+
|
186
|
+
features/lib/default/tasks/
|
187
|
+
├── add.rb
|
188
|
+
├── calculate.rb
|
189
|
+
├── calculations.rb
|
190
|
+
├── see_the_answer.rb
|
191
|
+
├── subtract.rb
|
192
|
+
└── switch_on_the_calculator.rb
|
193
|
+
|
194
|
+
You can structure your tasks as you see fit. For example, as the project grows, it might end up looking like this:
|
195
|
+
|
196
|
+
features/lib/default/tasks/
|
197
|
+
├── all_purpose
|
198
|
+
| └── calculate.rb
|
199
|
+
├── arithmetic
|
200
|
+
| ├── add.rb
|
201
|
+
| ├── divide.rb
|
202
|
+
| ├── multiply.rb
|
203
|
+
| └── subtract.rb
|
204
|
+
├── extras
|
205
|
+
| ├── recall_from_memory.rb
|
206
|
+
| ├── store_in_memory.rb
|
207
|
+
| └── switch_on_the_calculator.rb
|
208
|
+
├── questions
|
209
|
+
| ├── see_the_answer.rb
|
210
|
+
| ├── see_the_following_indicators.rb
|
211
|
+
| └── switch_on_the_calculator.rb
|
212
|
+
├── trigonometry
|
213
|
+
| ├── sine.rb
|
214
|
+
| ├── cosine.rb
|
215
|
+
| └── tangent.rb
|
216
|
+
└── reference_material
|
217
|
+
└── calculations.rb
|
218
|
+
|
219
|
+
## Alternative Roles
|
220
|
+
|
221
|
+
As our features _describe the value of a calculator application and not its
|
222
|
+
implementation_, we have the opportunity to reuse them.
|
223
|
+
|
224
|
+
In the Calculator example, we create a new _role_ in
|
225
|
+
`./features/lib/alternative/roles/calculating_web_user.rb`, which we can swap
|
226
|
+
into our tests using a Cucumber profile defined in `features/cucumber.yml`:
|
227
|
+
|
228
|
+
default --exclude features/lib/alternative/
|
229
|
+
alternative -r features/lib/alternative/ -r features/support/env.rb -r features/lib/default/tasks/
|
230
|
+
|
231
|
+
We can run our alternative configuration like so:
|
232
|
+
|
233
|
+
`%cucumber --profile alternative`
|
234
|
+
|
235
|
+
The Calculating Web User masquerades as the Calculating Individual from our
|
236
|
+
previous example, and provides the same API, allowing us to reuse all of our
|
237
|
+
existing features and _tasks_.
|
238
|
+
|
239
|
+
The alternative, `./lib/web_calculator.rb`, implementation is a simple [Sinatra](http://www.sinatrarb.com) application,
|
240
|
+
which we drive with the [Capybara](http://github.com/jnicklas/capybara) web testing framework.
|
241
|
+
|
242
|
+
By writing a single new _role_ class we're able to reuse all of our existing features,
|
243
|
+
_tasks_ and even the `Calculator` itself, which the web calculator delegates to in order to do its calculations.
|
244
|
+
|
245
|
+
After adding some more scenarios and tasks and an alternative "Calculating Individual" (see below for why), our Calculator directory structure currently looks like this...
|
246
|
+
|
247
|
+
├── cucumber.yml
|
248
|
+
├── features
|
249
|
+
│ ├── A_PlaceToStart.feature
|
250
|
+
│ ├── Addition.feature
|
251
|
+
│ ├── Complex_calculations.feature
|
252
|
+
│ ├── Subtraction.feature
|
253
|
+
│ ├── Typical_workflow.feature
|
254
|
+
│ ├── lib
|
255
|
+
│ │ ├── alternative
|
256
|
+
│ │ │ ├── roles
|
257
|
+
│ │ │ │ └── calculating_web_user.rb
|
258
|
+
│ │ │ └── tasks
|
259
|
+
│ │ └── default
|
260
|
+
│ │ ├── roles
|
261
|
+
│ │ │ └── calculating_individual.rb
|
262
|
+
│ │ └── tasks
|
263
|
+
│ │ ├── add.rb
|
264
|
+
│ │ ├── perform.rb
|
265
|
+
│ │ ├── see_the_answer.rb
|
266
|
+
│ │ ├── subtract.rb
|
267
|
+
│ │ └── switch_on_the_calculator.rb
|
268
|
+
│ └── support
|
269
|
+
│ └── env.rb
|
270
|
+
├── lib
|
271
|
+
│ ├── calculator.rb
|
272
|
+
│ └── web_calculator.rb
|
273
|
+
└── spec
|
274
|
+
├── calculator_spec.rb
|
275
|
+
└── web_calculator_spec.rb
|
276
|
+
|
277
|
+
Take a look around the examples and the code to see how it all works. We hope you enjoy reading as much as we enjoyed writing it.
|
@@ -1,34 +1,36 @@
|
|
1
1
|
Feature: Creating a New Project
|
2
2
|
As a Step Free Cuker
|
3
|
-
You want to set up your project to use use Cuke Salad
|
4
|
-
So that you can start writing scenarios without step definitions
|
3
|
+
You want to set up your project to use use Cuke Salad
|
4
|
+
So that you can start writing scenarios without step definitions
|
5
5
|
|
6
6
|
Scenario: Set up your project and verify that you can use CukeSalad
|
7
7
|
Given you are a Step Free Cuker
|
8
|
-
And you were able to
|
9
|
-
"""
|
10
|
-
features
|
11
|
-
features/lib
|
12
|
-
features/lib/roles
|
13
|
-
features/lib/tasks
|
14
|
-
features/support
|
15
|
-
"""
|
16
|
-
And you were able to create a file: at 'features/support/env.rb' containing
|
17
|
-
"""
|
18
|
-
$:.unshift(File.dirname(__FILE__) + "/../../../../lib") #where to find CukeSalad
|
19
|
-
|
20
|
-
require "cukesalad"
|
21
|
-
"""
|
8
|
+
And you were able to interactively run, the command 'cukesalad' and type 'y'
|
22
9
|
And you were able to create a file: at 'features/hello_cukesalad.feature' containing
|
23
10
|
"""
|
24
11
|
Feature: Hello CukeSalad
|
25
|
-
|
12
|
+
|
26
13
|
Scenario: Greetings
|
27
|
-
|
28
|
-
|
29
|
-
|
14
|
+
Given I am a Newbie
|
15
|
+
When I say hello world
|
16
|
+
"""
|
17
|
+
And you were able to create a file: at 'features/role/newbie.rb' containing
|
18
|
+
"""
|
19
|
+
module Newbie
|
20
|
+
def say_hello
|
21
|
+
puts 'CukeSalad says: Hello World!'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
"""
|
25
|
+
And you were able to create a file: at 'features/lib/hello_world.rb' containing
|
26
|
+
"""
|
27
|
+
in_order_to 'say hello world' do
|
28
|
+
say_hello
|
29
|
+
end
|
30
|
+
"""
|
31
|
+
When you attempt to run: the command 'cucumber'
|
30
32
|
Then you should see a reply that includes:
|
31
33
|
"""
|
32
|
-
CukeSalad says: Hello
|
34
|
+
CukeSalad says: Hello World!
|
33
35
|
"""
|
34
|
-
|
36
|
+
And you should see it has 'passed'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Your first step in your scenarios or in your background names a role relevant to your feature.
|
2
|
+
Cukesalad has an actor that adopts this role at run-time.
|
3
|
+
|
4
|
+
For example:
|
5
|
+
|
6
|
+
Given I am a Calculating Individual
|
7
|
+
|
8
|
+
This will find a role called `CalculatingIndividual`.
|
9
|
+
This role will have the methods that you call from within the tasks.
|
10
|
+
|
11
|
+
You would define this role as follows:
|
12
|
+
|
13
|
+
module CalculatingIndividual
|
14
|
+
def some_method_you_want_to_call
|
15
|
+
# some stuff the method does
|
16
|
+
end
|
17
|
+
...
|
18
|
+
end
|
19
|
+
|
20
|
+
That's all there is to it. You can simply wrap another module if you want to.
|
21
|
+
We've done this in our scenarios for cukesalad as follows:
|
22
|
+
|
23
|
+
module StepFreeCuker
|
24
|
+
include Aruba::Api
|
25
|
+
end
|
26
|
+
|
27
|
+
There are other things you can do, like initialise your 'role' by defining a `role_preparation` method.
|
28
|
+
|
29
|
+
Take a look through the examples in this folder.
|
@@ -28,3 +28,11 @@ Feature: Define the Role
|
|
28
28
|
Given I am a New Customer
|
29
29
|
"""
|
30
30
|
Then you should see it has 'passed'
|
31
|
+
|
32
|
+
Scenario: Roles which grammatically required an 'an'
|
33
|
+
Given you did create a role: called 'UnregisteredCustomer'
|
34
|
+
When you attempt to run a scenario: containing
|
35
|
+
"""
|
36
|
+
Given I am an Unregistered Customer
|
37
|
+
"""
|
38
|
+
Then you should see it has 'passed'
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
In your scenario, once you've named you role, you can write tasks that use methods on that role.
|
2
|
+
For example:
|
3
|
+
|
4
|
+
Given I was able to switch on the calculator
|
5
|
+
|
6
|
+
Needs a task called 'switch on the calculator'. You do this by creating a file, usually in `features/tasks`, called `switch_on_the_calculator.rb` with the following:
|
7
|
+
|
8
|
+
in_order_to "switch on the calculator" do
|
9
|
+
switch_on_the_calculator
|
10
|
+
end
|
11
|
+
|
12
|
+
This is the most basic example. Look through the other examples to see how to pass arguments into your task.
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Feature: Define
|
1
|
+
Feature: Define a Task
|
2
2
|
As a Step Free Cuker
|
3
3
|
You want to describe a task
|
4
4
|
So that your steps that use that role are executed
|
@@ -8,6 +8,23 @@ Feature: Define the Task
|
|
8
8
|
Given you are a Step Free Cuker
|
9
9
|
And you were able to create a role: called 'NewCustomer'
|
10
10
|
|
11
|
+
Scenario: We'll tell you what you need to do to describe the task
|
12
|
+
Given you did not create a task: called 'do something'
|
13
|
+
When you attempt to run a scenario: containing
|
14
|
+
"""
|
15
|
+
Given I am a New Customer
|
16
|
+
And I was able to do something
|
17
|
+
"""
|
18
|
+
Then you should see it has 'failed'
|
19
|
+
And you should see a reply that includes:
|
20
|
+
"""
|
21
|
+
I can't find a task called 'do something'. Have you created it?
|
22
|
+
e.g.
|
23
|
+
in_order_to 'do something' do
|
24
|
+
# the actions
|
25
|
+
end
|
26
|
+
"""
|
27
|
+
|
11
28
|
Scenario Outline: Once you've created the task, you see the step pass
|
12
29
|
Given you were able to create a task: called 'do something'
|
13
30
|
When you attempt to run a scenario: containing
|
@@ -35,20 +52,44 @@ Feature: Define the Task
|
|
35
52
|
| And I should do something |
|
36
53
|
| Then you should do something |
|
37
54
|
| And you should do something |
|
38
|
-
|
39
|
-
Scenario:
|
40
|
-
Given you
|
55
|
+
|
56
|
+
Scenario Outline: Once you've created the task, you see the step pass
|
57
|
+
Given you were able to create a task: called 'do something for 123Company'
|
41
58
|
When you attempt to run a scenario: containing
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
Then you should see it has '
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
59
|
+
"""
|
60
|
+
Given I am a New Customer
|
61
|
+
<step using the task>
|
62
|
+
"""
|
63
|
+
Then you should see it has 'passed'
|
64
|
+
|
65
|
+
Examples:
|
66
|
+
| step using the task |
|
67
|
+
| Given I was able to do something for 123Company |
|
68
|
+
| And I was able to do something for 123Company |
|
69
|
+
| But I did do something for 123Company |
|
70
|
+
| Given you were able to do something for 123Company |
|
71
|
+
| And you were able to do something for 123Company |
|
72
|
+
| But you did do something for 123Company |
|
73
|
+
| When I attempt to do something for 123Company |
|
74
|
+
| And I attempt to do something for 123Company |
|
75
|
+
| And I did do something for 123Company |
|
76
|
+
| When you attempt to do something for 123Company |
|
77
|
+
| And you attempt to do something for 123Company |
|
78
|
+
| And you did do something for 123Company |
|
79
|
+
| Then I should do something for 123Company |
|
80
|
+
| And I should do something for 123Company |
|
81
|
+
| Then you should do something for 123Company |
|
82
|
+
| And you should do something for 123Company |
|
83
|
+
|
84
|
+
Scenario Outline: Once you've created the task, you see the step pass
|
85
|
+
Given you were able to create a task: called 'have something'
|
86
|
+
When you attempt to run a scenario: containing
|
87
|
+
"""
|
88
|
+
Given I am a New Customer
|
89
|
+
<step using the task>
|
90
|
+
"""
|
91
|
+
Then you should see it has 'passed'
|
92
|
+
|
93
|
+
Examples:
|
94
|
+
| step using the task |
|
95
|
+
| Given I have something |
|