cukesalad 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/Examples/Calculator/Gemfile +4 -0
  2. data/Examples/Calculator/Gemfile.lock +36 -20
  3. data/Examples/Calculator/README.md +7 -0
  4. data/Examples/Calculator/cucumber.yml +1 -1
  5. data/Examples/Calculator/features/addition.feature +1 -1
  6. data/Examples/Calculator/features/lib/{alternative → browser}/roles/calculating_web_user.rb +1 -1
  7. data/Examples/Calculator/features/lib/default/roles/calculating_individual.rb +3 -5
  8. data/Examples/Calculator/features/lib/default/tasks/reference/calculations.rb +4 -2
  9. data/Examples/Calculator/features/subtraction.feature +1 -1
  10. data/Examples/Calculator/lib/calculator_operations.rb +4 -0
  11. data/Examples/Calculator/lib/web_calculator.rb +17 -20
  12. data/Examples/Calculator/views/index.erb +7 -0
  13. data/Examples/Calculator/views/layout.erb +2 -0
  14. data/Gemfile.lock +12 -12
  15. data/VERSION +1 -1
  16. data/cukesalad.gemspec +2 -2
  17. data/features/README.markdown +39 -273
  18. data/features/a_simple_tutorial/README.markdown +277 -0
  19. data/features/creating_a_new_project.feature +24 -22
  20. data/features/defining_roles/README.md +29 -0
  21. data/features/{define_a_role.feature → defining_roles/define_a_role.feature} +8 -0
  22. data/features/{prepare_the_actor_for_the_role.feature → defining_roles/prepare_the_actor_for_the_role.feature} +0 -0
  23. data/features/defining_tasks/README.md +12 -0
  24. data/features/{define_a_task.feature → defining_tasks/define_a_task.feature} +58 -17
  25. data/features/{define_a_task_with_arguments.feature → defining_tasks/define_a_task_with_arguments.feature} +0 -0
  26. data/features/{remember_information_between_steps.feature → defining_tasks/remember_information_between_steps.feature} +0 -0
  27. data/features/expectations/README.md +21 -0
  28. data/features/{expressing_expectations.feature → expectations/expressing_expectations.feature} +0 -0
  29. data/features/lib/tasks/interactively_run.rb +5 -0
  30. data/features/lib/tasks/see_the_step_has.rb +1 -1
  31. data/lib/cukesalad/actor.rb +5 -5
  32. data/lib/cukesalad/cli.rb +1 -1
  33. data/lib/cukesalad/cucumber_steps.rb +3 -7
  34. data/lib/cukesalad/specifics.rb +3 -3
  35. data/lib/cukesalad/version.rb +1 -1
  36. data/spec/cukesalad/cli_spec.rb +1 -1
  37. 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 create directories: as follows
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
- When I say hello CukeSalad
28
- """
29
- When you attempt to run: the command 'cucumber'
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
- And you should see it has 'passed'
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'
@@ -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 the Task
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: We'll tell you what you need to do to describe the task
40
- Given you did not create a task: called 'do something'
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
- Given I am a New Customer
44
- And I was able to do something
45
- """
46
- Then you should see it has 'failed'
47
- And you should see a reply that includes:
48
- """
49
- I can't find a task called 'do something'. Have you created it?
50
- e.g.
51
- in_order_to 'do something' do
52
- # the actions
53
- end
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 |