cukesalad 0.7.0 → 0.8.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.
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 |