rspec-given 2.1.0.beta.5 → 2.1.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 +12 -9
- data/examples/example_helper.rb +0 -4
- data/lib/rspec/given/version.rb +0 -2
- metadata +8 -7
- data/README +0 -367
- data/README.old +0 -367
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# rspec-given
|
2
2
|
|
3
|
-
Covering rspec-given, version 2.1.0.
|
3
|
+
Covering rspec-given, version 2.1.0.
|
4
4
|
|
5
5
|
rspec-given is an RSpec extension to allow Given/When/Then notation in
|
6
6
|
RSpec specifications. It is a natural extension of the experimental
|
@@ -156,9 +156,13 @@ The _When_ clause specifies the code to be tested ... oops, excuse me
|
|
156
156
|
... specified. After the preconditions in the given section are met,
|
157
157
|
the when code block is run.
|
158
158
|
|
159
|
-
|
160
|
-
context. However, a _When_ in an outer context
|
161
|
-
|
159
|
+
In general there should not be more than one _When_ clause for a given
|
160
|
+
direct context. However, a _When_ in an outer context will be run
|
161
|
+
after all the _Givens_ but before the inner _When_. You can think of
|
162
|
+
an outer _When_ as setting up additional given state for the inner
|
163
|
+
_When_.
|
164
|
+
|
165
|
+
E.g.
|
162
166
|
|
163
167
|
```ruby
|
164
168
|
context "outer context" do
|
@@ -301,11 +305,10 @@ stick with _Then_ clauses.
|
|
301
305
|
|
302
306
|
The _Invariant_ clause is a new idea that doesn't have an analog in
|
303
307
|
RSpec or Test::Unit. The invariant allows you specify things that must
|
304
|
-
always be true
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
false.
|
308
|
+
always be true in the scope of the invariant. In the stack example,
|
309
|
+
<tt>empty?</tt> is defined in term of <tt>size</tt>. Whenever
|
310
|
+
<tt>size</tt> is 0, <tt>empty?</tt> should be true. Whenever
|
311
|
+
<tt>size</tt> is non-zero, <tt>empty?</tt> should be false.
|
309
312
|
|
310
313
|
You can conceptually think of an _Invariant_ clause as a _Then_ block
|
311
314
|
that automatically gets added to every _Then_ within its scope.
|
data/examples/example_helper.rb
CHANGED
data/lib/rspec/given/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-given
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.0
|
5
|
-
prerelease:
|
4
|
+
version: 2.1.0
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jim Weirich
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -73,9 +73,7 @@ files:
|
|
73
73
|
- Gemfile.lock
|
74
74
|
- MIT-LICENSE
|
75
75
|
- Rakefile
|
76
|
-
- README
|
77
76
|
- README.md
|
78
|
-
- README.old
|
79
77
|
- lib/rspec-given.rb
|
80
78
|
- lib/rspec/given/configure.rb
|
81
79
|
- lib/rspec/given/extensions.rb
|
@@ -114,12 +112,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
112
|
- - ! '>='
|
115
113
|
- !ruby/object:Gem::Version
|
116
114
|
version: '0'
|
115
|
+
segments:
|
116
|
+
- 0
|
117
|
+
hash: -836562108368916480
|
117
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
119
|
none: false
|
119
120
|
requirements:
|
120
|
-
- - ! '
|
121
|
+
- - ! '>='
|
121
122
|
- !ruby/object:Gem::Version
|
122
|
-
version:
|
123
|
+
version: '0'
|
123
124
|
requirements: []
|
124
125
|
rubyforge_project: given
|
125
126
|
rubygems_version: 1.8.24
|
data/README
DELETED
@@ -1,367 +0,0 @@
|
|
1
|
-
= rspec-given
|
2
|
-
|
3
|
-
Covering rspec-given, version 2.1.0.beta.5.
|
4
|
-
|
5
|
-
rspec-given is an RSpec extension to allow Given/When/Then notation in
|
6
|
-
RSpec specifications. It is a natural extension of the experimental
|
7
|
-
work done on the Given framework. It turns out that 90% of the Given
|
8
|
-
framework can be trivially implemented on top of RSpec.
|
9
|
-
|
10
|
-
= Why Given/When/Then
|
11
|
-
|
12
|
-
RSpec has done a great job of making specifications more readable for
|
13
|
-
humans. However, I really like the given / when / then nature of
|
14
|
-
Cucumber stories and would like to follow the same structure in my
|
15
|
-
unit tests. rspec-given allows a simple given/when/then structure
|
16
|
-
RSpec specifications.
|
17
|
-
|
18
|
-
== Status
|
19
|
-
|
20
|
-
_rspec-given_ is ready for production use.
|
21
|
-
|
22
|
-
== Example
|
23
|
-
|
24
|
-
Here is a specification written in the rspec-given framework:
|
25
|
-
|
26
|
-
```ruby
|
27
|
-
require 'rspec/given'
|
28
|
-
require 'spec_helper'
|
29
|
-
require 'stack'
|
30
|
-
|
31
|
-
describe Stack do
|
32
|
-
def stack_with(initial_contents)
|
33
|
-
stack = Stack.new
|
34
|
-
initial_contents.each do |item| stack.push(item) end
|
35
|
-
stack
|
36
|
-
end
|
37
|
-
|
38
|
-
Given(:stack) { stack_with(initial_contents) }
|
39
|
-
Invariant { stack.empty?.should == (stack.depth == 0) }
|
40
|
-
|
41
|
-
context "when empty" do
|
42
|
-
Given(:initial_contents) { [] }
|
43
|
-
Then { stack.depth.should == 0 }
|
44
|
-
|
45
|
-
context "when pushing" do
|
46
|
-
When { stack.push(:an_item) }
|
47
|
-
|
48
|
-
Then { stack.depth.should == 1 }
|
49
|
-
Then { stack.top.should == :an_item }
|
50
|
-
end
|
51
|
-
|
52
|
-
context "when popping" do
|
53
|
-
When(:result) { stack.pop }
|
54
|
-
Then { result.should have_failed(Stack::UnderflowError, /empty/) }
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
context "with one item" do
|
59
|
-
Given(:initial_contents) { [:an_item] }
|
60
|
-
|
61
|
-
context "when popping" do
|
62
|
-
When(:pop_result) { stack.pop }
|
63
|
-
|
64
|
-
Then { pop_result.should == :an_item }
|
65
|
-
Then { stack.depth.should == 0 }
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
context "with several items" do
|
70
|
-
Given(:initial_contents) { [:second_item, :top_item] }
|
71
|
-
Given!(:original_depth) { stack.depth }
|
72
|
-
|
73
|
-
context "when pushing" do
|
74
|
-
When { stack.push(:new_item) }
|
75
|
-
|
76
|
-
Then { stack.top.should == :new_item }
|
77
|
-
Then { stack.depth.should == original_depth + 1 }
|
78
|
-
end
|
79
|
-
|
80
|
-
context "when popping" do
|
81
|
-
When(:pop_result) { stack.pop }
|
82
|
-
|
83
|
-
Then { pop_result.should == :top_item }
|
84
|
-
Then { stack.top.should == :second_item }
|
85
|
-
Then { stack.depth.should == original_depth - 1 }
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
```
|
90
|
-
|
91
|
-
Let's talk about the individual statements used in the Given
|
92
|
-
framework.
|
93
|
-
|
94
|
-
=== Given
|
95
|
-
|
96
|
-
The _Given_ section specifies a starting point, a set of preconditions
|
97
|
-
that must be true before the code under test is allowed to be run. In
|
98
|
-
standard test frameworks the preconditions are established with a
|
99
|
-
combination of setup methods (or :before actions in RSpec) and code in
|
100
|
-
the test.
|
101
|
-
|
102
|
-
In the example code above the preconditions are started with _Given_
|
103
|
-
statements. A top level _Given_ (that applies to the entire describe
|
104
|
-
block) says that one of the preconditions is that there is a stack
|
105
|
-
with some initial contents.
|
106
|
-
|
107
|
-
Note that initial contents are not specified in the top level describe
|
108
|
-
block, but are given in each of the nested contexts. By pushing the
|
109
|
-
definition of "initial_contents" into the nested contexts, we can vary
|
110
|
-
them as needed for that particular context.
|
111
|
-
|
112
|
-
A precondition in the form "Given(:var) {...}" creates an accessor
|
113
|
-
method named "var". The accessor is lazily initialized by the code
|
114
|
-
block. If you want a non-lazy given, use "Given!(:var) {...}".
|
115
|
-
|
116
|
-
A precondition in the form "Given {...}" just executes the code block
|
117
|
-
for side effects. Since there is no accessor, the code block is
|
118
|
-
executed immediately (i.e. no lazy evaluation).
|
119
|
-
|
120
|
-
The preconditions are run in order of definition. Nested contexts
|
121
|
-
will inherit the preconditions from the enclosing context, with out
|
122
|
-
preconditions running before inner preconditions.
|
123
|
-
|
124
|
-
==== Given examples:
|
125
|
-
|
126
|
-
```ruby
|
127
|
-
Given(:stack) { Stack.new }
|
128
|
-
```
|
129
|
-
|
130
|
-
The block for the given clause is lazily run if 'stack' is ever
|
131
|
-
referenced in the test and the value of the block is bound to 'stack'.
|
132
|
-
The first reference to 'stack' in the specification will cause the
|
133
|
-
code block to execute. Futher references to 'stack' will reuse the
|
134
|
-
previously generated value.
|
135
|
-
|
136
|
-
```ruby
|
137
|
-
Given!(:original_size) { stack.size }
|
138
|
-
```
|
139
|
-
|
140
|
-
The code block is run unconditionally once before each test and the
|
141
|
-
value of the block is bound to 'original_size'. This form is useful
|
142
|
-
when you want to record the value of something that might be affected
|
143
|
-
by the When code.
|
144
|
-
|
145
|
-
```ruby
|
146
|
-
Given { stack.clear }
|
147
|
-
```
|
148
|
-
|
149
|
-
The block for the given clause is run unconditionally once before each
|
150
|
-
test. This form of given is used for code that is executed for side
|
151
|
-
effects.
|
152
|
-
|
153
|
-
=== When
|
154
|
-
|
155
|
-
The _When_ clause specifies the code to be tested ... oops, excuse me
|
156
|
-
... specified. After the preconditions in the given section are met,
|
157
|
-
the when code block is run.
|
158
|
-
|
159
|
-
There should not be more than one _When_ clause for a given direct
|
160
|
-
context. However, a _When_ in an outer context shoud be treated as a
|
161
|
-
_Given_ in an inner context. E.g.
|
162
|
-
|
163
|
-
```ruby
|
164
|
-
context "outer context" do
|
165
|
-
When { code specified in the outer context }
|
166
|
-
Then { assert something about the outer context }
|
167
|
-
|
168
|
-
context "inner context" do
|
169
|
-
|
170
|
-
# At this point, the _When_ of the outer context
|
171
|
-
# should be treated as a _Given_ of the inner context
|
172
|
-
|
173
|
-
When { code specified in the inner context }
|
174
|
-
Then { assert something about the inner context }
|
175
|
-
end
|
176
|
-
end
|
177
|
-
```
|
178
|
-
|
179
|
-
==== When examples:
|
180
|
-
|
181
|
-
```ruby
|
182
|
-
When { stack.push(:item) }
|
183
|
-
```
|
184
|
-
|
185
|
-
The code block is executed once per test. The effect of the _When{}_
|
186
|
-
block is very similar to _Given{}_. However, When is used to identify
|
187
|
-
the particular code that is being specified in the current context or
|
188
|
-
describe block.
|
189
|
-
|
190
|
-
```ruby
|
191
|
-
When(:result) { stack.pop }
|
192
|
-
```
|
193
|
-
|
194
|
-
The code block is executed once per test and the value of the code
|
195
|
-
block is bound to 'result'. Use this form when the code under test
|
196
|
-
returns a value that you wish to interrogate in the _Then_ code.
|
197
|
-
|
198
|
-
If an exception occurs during the execution of the block for the When
|
199
|
-
clause, the exception is caught and a failure object is bound to
|
200
|
-
'result'. The failure can be checked in a then block with the
|
201
|
-
'have_failed' matcher.
|
202
|
-
|
203
|
-
The failure object will rethrow the captured exception if anything
|
204
|
-
other than have_failed matcher is used on the failure object.
|
205
|
-
|
206
|
-
For example, if the stack is empty when it is popped, then it is
|
207
|
-
reasonable for pop to raise an UnderflowError. This is how you might
|
208
|
-
specify that behavior:
|
209
|
-
|
210
|
-
```ruby
|
211
|
-
When(:result) { stack.pop }
|
212
|
-
Then { result.should have_failed(UnderflowError, /empty/) }
|
213
|
-
```
|
214
|
-
|
215
|
-
Note that the arguments to the 'have_failed' matcher are the same as
|
216
|
-
those given to the standard RSpec matcher 'raise_error'.
|
217
|
-
|
218
|
-
=== Then
|
219
|
-
|
220
|
-
The _Then_ clauses are the postconditions of the specification. These
|
221
|
-
then conditions must be true after the code under test (the _When_
|
222
|
-
clause) is run.
|
223
|
-
|
224
|
-
The code in the block of a _Then_ clause should be a single _should_
|
225
|
-
assertion. Code in _Then_ clauses should not have any side effects.
|
226
|
-
|
227
|
-
Let me repeat that: <b>_Then_ clauses should not have any side
|
228
|
-
effects!</b> _Then_ clauses with side effects are erroneous. _Then_
|
229
|
-
clauses need to be idempotent, so that running them once, twice, a
|
230
|
-
hundred times, or never does not change the state of the program. (The
|
231
|
-
same is true of _And_ clauses).
|
232
|
-
|
233
|
-
In RSpec terms, a _Then_ clause forms a RSpec Example that runs in the
|
234
|
-
context of an Example Group (defined by a describe or context clause).
|
235
|
-
|
236
|
-
Each Example Group must have at least one _Then_ clause, otherwise
|
237
|
-
there will be no examples to be run for that group. If all the
|
238
|
-
assertions in an example group are done via Invariants, then the group
|
239
|
-
should use an empty _Then_ clause, like this:
|
240
|
-
|
241
|
-
```ruby
|
242
|
-
Then { }
|
243
|
-
```
|
244
|
-
|
245
|
-
==== Then examples:
|
246
|
-
|
247
|
-
```ruby
|
248
|
-
Then { stack.should be_empty }
|
249
|
-
```
|
250
|
-
|
251
|
-
After the related block for the _When_ clause is run, the stack should
|
252
|
-
be empty. If it is not empty, the test will fail.
|
253
|
-
|
254
|
-
=== And
|
255
|
-
|
256
|
-
The _And_ clause is similar to _Then_, but does not form its own RSpec
|
257
|
-
example. This means that _And_ clauses reuse the setup from a sibling
|
258
|
-
_Then_ clause. Using a single _Then_ an multiple _And_ clauses in an
|
259
|
-
example group means the setup for that group is run only once (for the
|
260
|
-
_Then_ clause) and reused for all the _And_s. This can be a
|
261
|
-
significant speed savings where the setup for an example group is
|
262
|
-
expensive.
|
263
|
-
|
264
|
-
Some things to keep in mind about _And_ clauses:
|
265
|
-
|
266
|
-
1. There must be at least one _Then_ in the example group and it must
|
267
|
-
be declared before the _And_ clauses. Forgetting the _Then_ clause
|
268
|
-
is an error.
|
269
|
-
|
270
|
-
1. The code in the _And_ clause is run immediately after the first
|
271
|
-
(executed) _Then_ of an example group.
|
272
|
-
|
273
|
-
1. And assertion failures in a _Then_ clause or a _And_ clause will
|
274
|
-
cause all the subsequent _And_ clauses to be skipped.
|
275
|
-
|
276
|
-
1. Since _And_ clauses do not form their own RSpec examples, they are
|
277
|
-
not represented in the formatted output of RSpec. That means _And_
|
278
|
-
clauses do not produce dots in the Progress format, nor do they
|
279
|
-
appear in the documentation, html or textmate formats (options
|
280
|
-
-fhtml, -fdoc, or -ftextmate).
|
281
|
-
|
282
|
-
1. Like _Then_ clauses, _And_ clauses must be idempotent. That means
|
283
|
-
they should not execute any code that changes global program state.
|
284
|
-
(See the section on the _Then_ clause).
|
285
|
-
|
286
|
-
The choice to use an _And_ clause is primarily a speed consideration.
|
287
|
-
If an example group has expensive setup and there are a lot of _Then_
|
288
|
-
clauses, then choosing to make some of the _Then_ clauses into _And_
|
289
|
-
clause will speed up the spec. Otherwise it is probably better to
|
290
|
-
stick with _Then_ clauses.
|
291
|
-
|
292
|
-
==== Then/And examples:
|
293
|
-
|
294
|
-
```ruby
|
295
|
-
Then { pop_result.should == :top_item } # Required
|
296
|
-
And { stack.top.should == :second_item } # No Setup rerun
|
297
|
-
And { stack.depth.should == original_depth - 1 } # ... for these
|
298
|
-
```
|
299
|
-
|
300
|
-
=== Invariant
|
301
|
-
|
302
|
-
The _Invariant_ clause is a new idea that doesn't have an analog in
|
303
|
-
RSpec or Test::Unit. The invariant allows you specify things that must
|
304
|
-
always be true (or at least always be true in the scope of the
|
305
|
-
invariant). In the stack example, <tt>empty?</tt> is defined in term
|
306
|
-
of <tt>size</tt>. Whenever <tt>size</tt> is 0, <tt>empty?</tt> should
|
307
|
-
be true. Whenever <tt>size</tt> is non-zero, <tt>empty?</tt> should be
|
308
|
-
false.
|
309
|
-
|
310
|
-
You can conceptually think of an _Invariant_ clause as a _Then_ block
|
311
|
-
that automatically gets added to every _Then_ within its scope.
|
312
|
-
|
313
|
-
Invariants nested within a context only apply to the _Then_ clauses
|
314
|
-
that are in the scope of that context.
|
315
|
-
|
316
|
-
Invariants that reference a _Given_ precondition accessor must only be
|
317
|
-
used in contexts that define that accessor.
|
318
|
-
|
319
|
-
Notes:
|
320
|
-
|
321
|
-
1. Since Invariants do not form their own RSpec example, they are not
|
322
|
-
represented in the RSpec formatted output (e.g. the '--format html'
|
323
|
-
option).
|
324
|
-
|
325
|
-
== Configuration
|
326
|
-
|
327
|
-
Just require 'rspec/given' in the spec helper of your project and it
|
328
|
-
is ready to go.
|
329
|
-
|
330
|
-
If the RSpec format option document, html or textmate are chosen,
|
331
|
-
RSpec/Given will automatically add addition source code information to
|
332
|
-
the examples to produce better looking output. If you don't care about
|
333
|
-
the pretty output and wish to disable source code caching
|
334
|
-
unconditionally, then add the following line to your spec helper file:
|
335
|
-
|
336
|
-
```ruby
|
337
|
-
RSpec::Given.source_caching_disabled = true
|
338
|
-
```
|
339
|
-
|
340
|
-
= Future Directions
|
341
|
-
|
342
|
-
I really like the way the Given framework is working out. I feel my
|
343
|
-
tests are much more like specifications when I use it. However, I'm
|
344
|
-
not entirely happy with it.
|
345
|
-
|
346
|
-
I would like to remove the need for the ".should" in all the _Then_
|
347
|
-
clauses. In other words, instead of saying:
|
348
|
-
|
349
|
-
```ruby
|
350
|
-
Then { x.should == y }
|
351
|
-
```
|
352
|
-
|
353
|
-
we could say:
|
354
|
-
|
355
|
-
```ruby
|
356
|
-
Then { x == y }
|
357
|
-
```
|
358
|
-
|
359
|
-
I think the [wrong assertion library](http://rubygems.org/gems/wrong)
|
360
|
-
has laid some groundwork in this area.
|
361
|
-
|
362
|
-
= Links
|
363
|
-
|
364
|
-
* Github: [https://github.com/jimweirich/rspec-given](https://github.com/jimweirich/rspec-given)
|
365
|
-
* Clone URL: git://github.com/jimweirich/rspec-given.git
|
366
|
-
* Bug/Issue Reporting: [https://github.com/jimweirich/rspec-given/issues](https://github.com/jimweirich/rspec-given/issues)
|
367
|
-
* Continuous Integration: [http://travis-ci.org/#!/jimweirich/rspec-given](http://travis-ci.org/#!/jimweirich/rspec-given)
|
data/README.old
DELETED
@@ -1,367 +0,0 @@
|
|
1
|
-
# rspec-given
|
2
|
-
|
3
|
-
Covering rspec-given, version 2.1.0.beta.4.
|
4
|
-
|
5
|
-
rspec-given is an RSpec extension to allow Given/When/Then notation in
|
6
|
-
RSpec specifications. It is a natural extension of the experimental
|
7
|
-
work done on the Given framework. It turns out that 90% of the Given
|
8
|
-
framework can be trivially implemented on top of RSpec.
|
9
|
-
|
10
|
-
# Why Given/When/Then
|
11
|
-
|
12
|
-
RSpec has done a great job of making specifications more readable for
|
13
|
-
humans. However, I really like the given / when / then nature of
|
14
|
-
Cucumber stories and would like to follow the same structure in my
|
15
|
-
unit tests. rspec-given allows a simple given/when/then structure
|
16
|
-
RSpec specifications.
|
17
|
-
|
18
|
-
## Status
|
19
|
-
|
20
|
-
_rspec-given_ is ready for production use.
|
21
|
-
|
22
|
-
## Example
|
23
|
-
|
24
|
-
Here is a specification written in the rspec-given framework:
|
25
|
-
|
26
|
-
```ruby
|
27
|
-
require 'rspec/given'
|
28
|
-
require 'spec_helper'
|
29
|
-
require 'stack'
|
30
|
-
|
31
|
-
describe Stack do
|
32
|
-
def stack_with(initial_contents)
|
33
|
-
stack = Stack.new
|
34
|
-
initial_contents.each do |item| stack.push(item) end
|
35
|
-
stack
|
36
|
-
end
|
37
|
-
|
38
|
-
Given(:stack) { stack_with(initial_contents) }
|
39
|
-
Invariant { stack.empty?.should == (stack.depth == 0) }
|
40
|
-
|
41
|
-
context "when empty" do
|
42
|
-
Given(:initial_contents) { [] }
|
43
|
-
Then { stack.depth.should == 0 }
|
44
|
-
|
45
|
-
context "when pushing" do
|
46
|
-
When { stack.push(:an_item) }
|
47
|
-
|
48
|
-
Then { stack.depth.should == 1 }
|
49
|
-
Then { stack.top.should == :an_item }
|
50
|
-
end
|
51
|
-
|
52
|
-
context "when popping" do
|
53
|
-
When(:result) { stack.pop }
|
54
|
-
Then { result.should have_failed(Stack::UnderflowError, /empty/) }
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
context "with one item" do
|
59
|
-
Given(:initial_contents) { [:an_item] }
|
60
|
-
|
61
|
-
context "when popping" do
|
62
|
-
When(:pop_result) { stack.pop }
|
63
|
-
|
64
|
-
Then { pop_result.should == :an_item }
|
65
|
-
Then { stack.depth.should == 0 }
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
context "with several items" do
|
70
|
-
Given(:initial_contents) { [:second_item, :top_item] }
|
71
|
-
Given!(:original_depth) { stack.depth }
|
72
|
-
|
73
|
-
context "when pushing" do
|
74
|
-
When { stack.push(:new_item) }
|
75
|
-
|
76
|
-
Then { stack.top.should == :new_item }
|
77
|
-
Then { stack.depth.should == original_depth + 1 }
|
78
|
-
end
|
79
|
-
|
80
|
-
context "when popping" do
|
81
|
-
When(:pop_result) { stack.pop }
|
82
|
-
|
83
|
-
Then { pop_result.should == :top_item }
|
84
|
-
Then { stack.top.should == :second_item }
|
85
|
-
Then { stack.depth.should == original_depth - 1 }
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
```
|
90
|
-
|
91
|
-
Let's talk about the individual statements used in the Given
|
92
|
-
framework.
|
93
|
-
|
94
|
-
### Given
|
95
|
-
|
96
|
-
The _Given_ section specifies a starting point, a set of preconditions
|
97
|
-
that must be true before the code under test is allowed to be run. In
|
98
|
-
standard test frameworks the preconditions are established with a
|
99
|
-
combination of setup methods (or :before actions in RSpec) and code in
|
100
|
-
the test.
|
101
|
-
|
102
|
-
In the example code above the preconditions are started with _Given_
|
103
|
-
statements. A top level _Given_ (that applies to the entire describe
|
104
|
-
block) says that one of the preconditions is that there is a stack
|
105
|
-
with some initial contents.
|
106
|
-
|
107
|
-
Note that initial contents are not specified in the top level describe
|
108
|
-
block, but are given in each of the nested contexts. By pushing the
|
109
|
-
definition of "initial_contents" into the nested contexts, we can vary
|
110
|
-
them as needed for that particular context.
|
111
|
-
|
112
|
-
A precondition in the form "Given(:var) {...}" creates an accessor
|
113
|
-
method named "var". The accessor is lazily initialized by the code
|
114
|
-
block. If you want a non-lazy given, use "Given!(:var) {...}".
|
115
|
-
|
116
|
-
A precondition in the form "Given {...}" just executes the code block
|
117
|
-
for side effects. Since there is no accessor, the code block is
|
118
|
-
executed immediately (i.e. no lazy evaluation).
|
119
|
-
|
120
|
-
The preconditions are run in order of definition. Nested contexts
|
121
|
-
will inherit the preconditions from the enclosing context, with out
|
122
|
-
preconditions running before inner preconditions.
|
123
|
-
|
124
|
-
#### Given examples:
|
125
|
-
|
126
|
-
```ruby
|
127
|
-
Given(:stack) { Stack.new }
|
128
|
-
```
|
129
|
-
|
130
|
-
The block for the given clause is lazily run if 'stack' is ever
|
131
|
-
referenced in the test and the value of the block is bound to 'stack'.
|
132
|
-
The first reference to 'stack' in the specification will cause the
|
133
|
-
code block to execute. Futher references to 'stack' will reuse the
|
134
|
-
previously generated value.
|
135
|
-
|
136
|
-
```ruby
|
137
|
-
Given!(:original_size) { stack.size }
|
138
|
-
```
|
139
|
-
|
140
|
-
The code block is run unconditionally once before each test and the
|
141
|
-
value of the block is bound to 'original_size'. This form is useful
|
142
|
-
when you want to record the value of something that might be affected
|
143
|
-
by the When code.
|
144
|
-
|
145
|
-
```ruby
|
146
|
-
Given { stack.clear }
|
147
|
-
```
|
148
|
-
|
149
|
-
The block for the given clause is run unconditionally once before each
|
150
|
-
test. This form of given is used for code that is executed for side
|
151
|
-
effects.
|
152
|
-
|
153
|
-
### When
|
154
|
-
|
155
|
-
The _When_ clause specifies the code to be tested ... oops, excuse me
|
156
|
-
... specified. After the preconditions in the given section are met,
|
157
|
-
the when code block is run.
|
158
|
-
|
159
|
-
There should not be more than one _When_ clause for a given direct
|
160
|
-
context. However, a _When_ in an outer context shoud be treated as a
|
161
|
-
_Given_ in an inner context. E.g.
|
162
|
-
|
163
|
-
```ruby
|
164
|
-
context "outer context" do
|
165
|
-
When { code specified in the outer context }
|
166
|
-
Then { assert something about the outer context }
|
167
|
-
|
168
|
-
context "inner context" do
|
169
|
-
|
170
|
-
# At this point, the _When_ of the outer context
|
171
|
-
# should be treated as a _Given_ of the inner context
|
172
|
-
|
173
|
-
When { code specified in the inner context }
|
174
|
-
Then { assert something about the inner context }
|
175
|
-
end
|
176
|
-
end
|
177
|
-
```
|
178
|
-
|
179
|
-
#### When examples:
|
180
|
-
|
181
|
-
```ruby
|
182
|
-
When { stack.push(:item) }
|
183
|
-
```
|
184
|
-
|
185
|
-
The code block is executed once per test. The effect of the _When{}_
|
186
|
-
block is very similar to _Given{}_. However, When is used to identify
|
187
|
-
the particular code that is being specified in the current context or
|
188
|
-
describe block.
|
189
|
-
|
190
|
-
```ruby
|
191
|
-
When(:result) { stack.pop }
|
192
|
-
```
|
193
|
-
|
194
|
-
The code block is executed once per test and the value of the code
|
195
|
-
block is bound to 'result'. Use this form when the code under test
|
196
|
-
returns a value that you wish to interrogate in the _Then_ code.
|
197
|
-
|
198
|
-
If an exception occurs during the execution of the block for the When
|
199
|
-
clause, the exception is caught and a failure object is bound to
|
200
|
-
'result'. The failure can be checked in a then block with the
|
201
|
-
'have_failed' matcher.
|
202
|
-
|
203
|
-
The failure object will rethrow the captured exception if anything
|
204
|
-
other than have_failed matcher is used on the failure object.
|
205
|
-
|
206
|
-
For example, if the stack is empty when it is popped, then it is
|
207
|
-
reasonable for pop to raise an UnderflowError. This is how you might
|
208
|
-
specify that behavior:
|
209
|
-
|
210
|
-
```ruby
|
211
|
-
When(:result) { stack.pop }
|
212
|
-
Then { result.should have_failed(UnderflowError, /empty/) }
|
213
|
-
```
|
214
|
-
|
215
|
-
Note that the arguments to the 'have_failed' matcher are the same as
|
216
|
-
those given to the standard RSpec matcher 'raise_error'.
|
217
|
-
|
218
|
-
### Then
|
219
|
-
|
220
|
-
The _Then_ clauses are the postconditions of the specification. These
|
221
|
-
then conditions must be true after the code under test (the _When_
|
222
|
-
clause) is run.
|
223
|
-
|
224
|
-
The code in the block of a _Then_ clause should be a single _should_
|
225
|
-
assertion. Code in _Then_ clauses should not have any side effects.
|
226
|
-
|
227
|
-
Let me repeat that: <b>_Then_ clauses should not have any side
|
228
|
-
effects!</b> _Then_ clauses with side effects are erroneous. _Then_
|
229
|
-
clauses need to be idempotent, so that running them once, twice, a
|
230
|
-
hundred times, or never does not change the state of the program. (The
|
231
|
-
same is true of _And_ clauses).
|
232
|
-
|
233
|
-
In RSpec terms, a _Then_ clause forms a RSpec Example that runs in the
|
234
|
-
context of an Example Group (defined by a describe or context clause).
|
235
|
-
|
236
|
-
Each Example Group must have at least one _Then_ clause, otherwise
|
237
|
-
there will be no examples to be run for that group. If all the
|
238
|
-
assertions in an example group are done via Invariants, then the group
|
239
|
-
should use an empty _Then_ clause, like this:
|
240
|
-
|
241
|
-
```ruby
|
242
|
-
Then { }
|
243
|
-
```
|
244
|
-
|
245
|
-
#### Then examples:
|
246
|
-
|
247
|
-
```ruby
|
248
|
-
Then { stack.should be_empty }
|
249
|
-
```
|
250
|
-
|
251
|
-
After the related block for the _When_ clause is run, the stack should
|
252
|
-
be empty. If it is not empty, the test will fail.
|
253
|
-
|
254
|
-
### And
|
255
|
-
|
256
|
-
The _And_ clause is similar to _Then_, but does not form its own RSpec
|
257
|
-
example. This means that _And_ clauses reuse the setup from a sibling
|
258
|
-
_Then_ clause. Using a single _Then_ an multiple _And_ clauses in an
|
259
|
-
example group means the setup for that group is run only once (for the
|
260
|
-
_Then_ clause) and reused for all the _And_s. This can be a
|
261
|
-
significant speed savings where the setup for an example group is
|
262
|
-
expensive.
|
263
|
-
|
264
|
-
Some things to keep in mind about _And_ clauses:
|
265
|
-
|
266
|
-
1. There must be at least one _Then_ in the example group and it must
|
267
|
-
be declared before the _And_ clauses. Forgetting the _Then_ clause
|
268
|
-
is an error.
|
269
|
-
|
270
|
-
1. The code in the _And_ clause is run immediately after the first
|
271
|
-
(executed) _Then_ of an example group.
|
272
|
-
|
273
|
-
1. And assertion failures in a _Then_ clause or a _And_ clause will
|
274
|
-
cause all the subsequent _And_ clauses to be skipped.
|
275
|
-
|
276
|
-
1. Since _And_ clauses do not form their own RSpec examples, they are
|
277
|
-
not represented in the formatted output of RSpec. That means _And_
|
278
|
-
clauses do not produce dots in the Progress format, nor do they
|
279
|
-
appear in the documentation, html or textmate formats (options
|
280
|
-
-fhtml, -fdoc, or -ftextmate).
|
281
|
-
|
282
|
-
1. Like _Then_ clauses, _And_ clauses must be idempotent. That means
|
283
|
-
they should not execute any code that changes global program state.
|
284
|
-
(See the section on the _Then_ clause).
|
285
|
-
|
286
|
-
The choice to use an _And_ clause is primarily a speed consideration.
|
287
|
-
If an example group has expensive setup and there are a lot of _Then_
|
288
|
-
clauses, then choosing to make some of the _Then_ clauses into _And_
|
289
|
-
clause will speed up the spec. Otherwise it is probably better to
|
290
|
-
stick with _Then_ clauses.
|
291
|
-
|
292
|
-
#### Then/And examples:
|
293
|
-
|
294
|
-
```ruby
|
295
|
-
Then { pop_result.should == :top_item } # Required
|
296
|
-
And { stack.top.should == :second_item } # No Setup rerun
|
297
|
-
And { stack.depth.should == original_depth - 1 } # ... for these
|
298
|
-
```
|
299
|
-
|
300
|
-
### Invariant
|
301
|
-
|
302
|
-
The _Invariant_ clause is a new idea that doesn't have an analog in
|
303
|
-
RSpec or Test::Unit. The invariant allows you specify things that must
|
304
|
-
always be true (or at least always be true in the scope of the
|
305
|
-
invariant). In the stack example, <tt>empty?</tt> is defined in term
|
306
|
-
of <tt>size</tt>. Whenever <tt>size</tt> is 0, <tt>empty?</tt> should
|
307
|
-
be true. Whenever <tt>size</tt> is non-zero, <tt>empty?</tt> should be
|
308
|
-
false.
|
309
|
-
|
310
|
-
You can conceptually think of an _Invariant_ clause as a _Then_ block
|
311
|
-
that automatically gets added to every _Then_ within its scope.
|
312
|
-
|
313
|
-
Invariants nested within a context only apply to the _Then_ clauses
|
314
|
-
that are in the scope of that context.
|
315
|
-
|
316
|
-
Invariants that reference a _Given_ precondition accessor must only be
|
317
|
-
used in contexts that define that accessor.
|
318
|
-
|
319
|
-
Notes:
|
320
|
-
|
321
|
-
1. Since Invariants do not form their own RSpec example, they are not
|
322
|
-
represented in the RSpec formatted output (e.g. the '--format html'
|
323
|
-
option).
|
324
|
-
|
325
|
-
## Configuration
|
326
|
-
|
327
|
-
Just require 'rspec/given' in the spec helper of your project and it
|
328
|
-
is ready to go.
|
329
|
-
|
330
|
-
If the RSpec format option document, html or textmate are chosen,
|
331
|
-
RSpec/Given will automatically add addition source code information to
|
332
|
-
the examples to produce better looking output. If you don't care about
|
333
|
-
the pretty output and wish to disable source code caching
|
334
|
-
unconditionally, then add the following line to your spec helper file:
|
335
|
-
|
336
|
-
```ruby
|
337
|
-
RSpec::Given.source_caching_disabled = true
|
338
|
-
```
|
339
|
-
|
340
|
-
# Future Directions
|
341
|
-
|
342
|
-
I really like the way the Given framework is working out. I feel my
|
343
|
-
tests are much more like specifications when I use it. However, I'm
|
344
|
-
not entirely happy with it.
|
345
|
-
|
346
|
-
I would like to remove the need for the ".should" in all the _Then_
|
347
|
-
clauses. In other words, instead of saying:
|
348
|
-
|
349
|
-
```ruby
|
350
|
-
Then { x.should == y }
|
351
|
-
```
|
352
|
-
|
353
|
-
we could say:
|
354
|
-
|
355
|
-
```ruby
|
356
|
-
Then { x == y }
|
357
|
-
```
|
358
|
-
|
359
|
-
I think the [wrong assertion library](http://rubygems.org/gems/wrong)
|
360
|
-
has laid some groundwork in this area.
|
361
|
-
|
362
|
-
# Links
|
363
|
-
|
364
|
-
* Github: [https://github.com/jimweirich/rspec-given](https://github.com/jimweirich/rspec-given)
|
365
|
-
* Clone URL: git://github.com/jimweirich/rspec-given.git
|
366
|
-
* Bug/Issue Reporting: [https://github.com/jimweirich/rspec-given/issues](https://github.com/jimweirich/rspec-given/issues)
|
367
|
-
* Continuous Integration: [http://travis-ci.org/#!/jimweirich/rspec-given](http://travis-ci.org/#!/jimweirich/rspec-given)
|