rspec 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +57 -32
- data/EXAMPLES.rd +0 -0
- data/Rakefile +22 -21
- data/bin/spec +9 -11
- data/doc/README +1 -3
- data/doc/plugin/syntax.rb +27 -5
- data/doc/src/core_team.page +22 -0
- data/doc/src/default.css +11 -11
- data/doc/src/default.template +0 -1
- data/doc/src/documentation/index.page +183 -8
- data/doc/src/documentation/meta.info +7 -7
- data/doc/src/documentation/mocks.page +168 -109
- data/doc/src/documentation/underscores.page +20 -0
- data/doc/src/examples.page +2 -1
- data/doc/src/images/David_and_Aslak.jpg +0 -0
- data/doc/src/images/Whats_That_Dude.jpg +0 -0
- data/doc/src/index.page +70 -3
- data/doc/src/meta.info +18 -11
- data/doc/src/tools/index.page +40 -134
- data/doc/src/tools/meta.info +9 -3
- data/doc/src/tools/rails.page +3 -1
- data/doc/src/tools/rake.page +20 -3
- data/doc/src/tools/rcov.page +19 -0
- data/doc/src/tools/spec.page +99 -0
- data/doc/src/tools/test2rspec.page +2 -4
- data/doc/src/tutorials/index.page +52 -0
- data/doc/src/tutorials/meta.info +31 -0
- data/doc/src/tutorials/notes.txt +252 -0
- data/doc/src/tutorials/stack.rb +11 -0
- data/doc/src/tutorials/stack_01.page +224 -0
- data/doc/src/tutorials/stack_02.page +180 -0
- data/doc/src/tutorials/stack_03.page +291 -0
- data/doc/src/tutorials/stack_04.page +203 -0
- data/doc/src/tutorials/stack_04.page.orig +123 -0
- data/doc/src/tutorials/stack_05.page +90 -0
- data/doc/src/tutorials/stack_05.page.orig +124 -0
- data/doc/src/tutorials/stack_06.page +359 -0
- data/doc/src/tutorials/stack_06.page.orig +359 -0
- data/doc/src/tutorials/stack_spec.rb +41 -0
- data/examples/airport_spec.rb +4 -4
- data/examples/{spec_framework_spec.rb → bdd_framework_spec.rb} +6 -7
- data/examples/mocking_spec.rb +0 -5
- data/examples/stack_spec.rb +6 -7
- data/examples/sugar_spec.rb +14 -0
- data/lib/spec/api.rb +5 -2
- data/lib/spec/api/helper/should_base.rb +17 -22
- data/lib/spec/api/helper/should_helper.rb +4 -3
- data/lib/spec/api/helper/should_negator.rb +3 -2
- data/lib/spec/api/mocks/argument_expectation.rb +104 -0
- data/lib/spec/api/{mock.rb → mocks/message_expectation.rb} +47 -96
- data/lib/spec/api/mocks/mock.rb +63 -0
- data/lib/spec/api/mocks/order_group.rb +21 -0
- data/lib/spec/api/sugar.rb +47 -0
- data/lib/spec/rake/rcov_verify.rb +45 -0
- data/lib/spec/rake/spectask.rb +41 -56
- data/lib/spec/runner.rb +4 -1
- data/lib/spec/runner/backtrace_tweaker.rb +24 -3
- data/lib/spec/runner/base_text_formatter.rb +28 -0
- data/lib/spec/runner/context.rb +21 -18
- data/lib/spec/runner/context_runner.rb +20 -31
- data/lib/spec/runner/execution_context.rb +3 -3
- data/lib/spec/runner/kernel_ext.rb +10 -1
- data/lib/spec/runner/option_parser.rb +32 -14
- data/lib/spec/runner/progress_bar_formatter.rb +21 -0
- data/lib/spec/runner/rdoc_formatter.rb +15 -5
- data/lib/spec/runner/reporter.rb +100 -0
- data/lib/spec/runner/specdoc_formatter.rb +20 -0
- data/lib/spec/runner/specification.rb +42 -22
- data/lib/spec/version.rb +1 -1
- data/test/rcov/rcov_testtask.rb +1 -0
- data/test/spec/api/duck_type_test.rb +4 -4
- data/test/spec/api/helper/raising_test.rb +37 -17
- data/test/spec/api/{mock_arg_constraints_test.rb → mocks/mock_arg_constraints_test.rb} +10 -4
- data/test/spec/api/mocks/mock_ordering_test.rb +62 -0
- data/test/spec/api/{mock_test.rb → mocks/mock_test.rb} +30 -7
- data/test/spec/api/mocks/null_object_test.rb +31 -0
- data/test/spec/api/sugar_test.rb +71 -0
- data/test/spec/runner/backtrace_tweaker_test.rb +52 -4
- data/test/spec/runner/context_runner_test.rb +41 -21
- data/test/spec/runner/context_test.rb +60 -32
- data/test/spec/runner/execution_context_test.rb +4 -3
- data/test/spec/runner/failure_dump_test.rb +92 -0
- data/test/spec/runner/kernel_ext_test.rb +1 -2
- data/test/spec/runner/option_parser_test.rb +48 -28
- data/test/spec/runner/progress_bar_formatter_test.rb +48 -0
- data/test/spec/runner/rdoc_formatter_test.rb +31 -4
- data/test/spec/runner/reporter_test.rb +103 -0
- data/test/spec/runner/specdoc_formatter_test.rb +50 -0
- data/test/spec/runner/specification_test.rb +49 -11
- data/test/test_helper.rb +1 -4
- metadata +46 -15
- data/doc/src/community.page +0 -7
- data/doc/src/documentation/api.page +0 -185
- data/doc/src/why_rspec.page +0 -7
- data/examples/empty_stack_spec.rb +0 -22
- data/examples/team_spec.rb +0 -30
- data/lib/spec/api/duck_type.rb +0 -16
- data/lib/spec/runner/simple_text_reporter.rb +0 -88
- data/test/rcov/rcov_verify.rb +0 -28
- data/test/spec/runner/simple_text_reporter_test.rb +0 -123
@@ -0,0 +1,203 @@
|
|
1
|
+
h2. A Simple Stack - when a new expectation passes
|
2
|
+
|
3
|
+
At this point stack_spec.rb should look like this...
|
4
|
+
|
5
|
+
<ruby>
|
6
|
+
require 'stack'
|
7
|
+
|
8
|
+
context "A new stack" do
|
9
|
+
setup do
|
10
|
+
@stack = Stack.new
|
11
|
+
end
|
12
|
+
|
13
|
+
specify "should be empty" do
|
14
|
+
@stack.should_be_empty
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "An empty stack" do
|
19
|
+
setup do
|
20
|
+
@stack = Stack.new
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "should keep its mouth shut when you send it 'push'" do
|
24
|
+
lambda { @stack.push Object.new }.should.not.raise
|
25
|
+
end
|
26
|
+
|
27
|
+
specify "should not be empty after 'push'" do
|
28
|
+
@stack.push 37
|
29
|
+
@stack.should_not_be_empty
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "A stack with one item" do
|
34
|
+
setup do
|
35
|
+
@stack = Stack.new
|
36
|
+
@stack.push "one item"
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "should return top when you send it 'top'" do
|
40
|
+
@stack.top.should_equal "one item"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
</ruby>
|
44
|
+
|
45
|
+
stack.rb should look like this...
|
46
|
+
|
47
|
+
<ruby>
|
48
|
+
class Stack
|
49
|
+
def empty?
|
50
|
+
@item.nil?
|
51
|
+
end
|
52
|
+
def push item
|
53
|
+
@item = item
|
54
|
+
end
|
55
|
+
def top
|
56
|
+
@item
|
57
|
+
end
|
58
|
+
end
|
59
|
+
</ruby>
|
60
|
+
|
61
|
+
...and the output should look like this:
|
62
|
+
|
63
|
+
<pre>
|
64
|
+
$ spec stack_spec.rb -f s
|
65
|
+
|
66
|
+
A new stack
|
67
|
+
- should be empty
|
68
|
+
|
69
|
+
An empty stack
|
70
|
+
- should keep its mouth shut when you send it 'push'
|
71
|
+
- should not be empty after 'push'
|
72
|
+
|
73
|
+
A stack with one item
|
74
|
+
- should return top when you send it 'top'
|
75
|
+
|
76
|
+
Finished in 0.000741 seconds
|
77
|
+
|
78
|
+
3 contexts, 4 specifications, 0 failures
|
79
|
+
</pre>
|
80
|
+
|
81
|
+
Again, a really nice feature of using the <code>--format specdoc</code> option (<code>-f s</code> for short) is that you can easily see all of the contexts you've specified and the balance of specifications between contexts. As your specification is evolving, this helps to inform you regarding the next step. Comparing "An empty stack" to "A stack with one item", there are a couple of ways to go here, which is nice (better than having no obvious next step). On "An empty stack", we know two things about the 'push' message. Let's balance "A stack with one item" with that, and specify the 'emptiness' of the stack after 'top'.
|
82
|
+
|
83
|
+
<ruby>
|
84
|
+
context "A stack with one item" do
|
85
|
+
setup do
|
86
|
+
@stack = Stack.new
|
87
|
+
@stack.push "one item"
|
88
|
+
end
|
89
|
+
|
90
|
+
...
|
91
|
+
|
92
|
+
specify "should not be empty after 'top'" do
|
93
|
+
@stack.top
|
94
|
+
@stack.should_not_be_empty
|
95
|
+
end
|
96
|
+
end
|
97
|
+
</ruby>
|
98
|
+
<br>
|
99
|
+
<pre>
|
100
|
+
$ spec stack_spec.rb -f s
|
101
|
+
|
102
|
+
A new stack
|
103
|
+
- should be empty
|
104
|
+
|
105
|
+
An empty stack
|
106
|
+
- should keep its mouth shut when you send it 'push'
|
107
|
+
- should not be empty after 'push'
|
108
|
+
|
109
|
+
A stack with one item
|
110
|
+
- should return top when you send it 'top'
|
111
|
+
- should not be empty after 'top'
|
112
|
+
|
113
|
+
Finished in 0.000853 seconds
|
114
|
+
|
115
|
+
3 contexts, 5 specifications, 0 failures
|
116
|
+
</pre>
|
117
|
+
|
118
|
+
This one passes right away. This situation comes up a lot as you progress through a specification. You add a specification and the expectations are already met. You never saw the failure, so how do you know that you wrote the specification correctly, or that it exercises the code that you want to exercise? We should see the expectation fail before moving on. So now the question is what is the best approach to making it fail? Well, we want to see the failure so that when we make a change to the code to make it pass, we know that we've exercised the right place in the code from the spec. To ensure that, we really want to make the change in the code.
|
119
|
+
|
120
|
+
In this case, the simplest way to approach this is to return <code>true</code> from <code>empty?</code>.
|
121
|
+
|
122
|
+
<ruby>
|
123
|
+
class Stack
|
124
|
+
def empty?
|
125
|
+
true
|
126
|
+
#@item.nil?
|
127
|
+
end
|
128
|
+
def push item
|
129
|
+
@item = item
|
130
|
+
end
|
131
|
+
def top
|
132
|
+
@item
|
133
|
+
end
|
134
|
+
end
|
135
|
+
</ruby>
|
136
|
+
<br>
|
137
|
+
<pre>
|
138
|
+
$ spec stack_spec.rb -f s -s "A stack with one item should not be empty after 'top'"
|
139
|
+
|
140
|
+
A stack with one item
|
141
|
+
- should not be empty after 'top' (FAILED - 1)
|
142
|
+
|
143
|
+
1)
|
144
|
+
ExpectationNotMetError in 'A stack with one item should not be empty after 'top''
|
145
|
+
Stack #<Stack:0x36ad18 @item="one item"> should not be empty
|
146
|
+
./stack_spec.rb:39:in `should not be empty after 'top''
|
147
|
+
|
148
|
+
Finished in 0.000398 seconds
|
149
|
+
|
150
|
+
1 context, 1 specification, 1 failure
|
151
|
+
</pre>
|
152
|
+
|
153
|
+
Here we used the <code>-s</code> option, which allows you to specify a single specification by supplying its full name (context name + space + spec name).
|
154
|
+
|
155
|
+
The failure tells us that there was an ExpectationNotMetError, as we expected. Revert the code ...
|
156
|
+
|
157
|
+
<ruby>
|
158
|
+
class Stack
|
159
|
+
def empty?
|
160
|
+
@item.nil?
|
161
|
+
end
|
162
|
+
...
|
163
|
+
end
|
164
|
+
</ruby>
|
165
|
+
|
166
|
+
... run the single spec ...
|
167
|
+
|
168
|
+
<pre>
|
169
|
+
$ spec stack_spec.rb -f s -s "A stack with one item should not be empty after 'top'"
|
170
|
+
|
171
|
+
A stack with one item
|
172
|
+
- should not be empty after 'top'
|
173
|
+
|
174
|
+
Finished in 0.00028 seconds
|
175
|
+
|
176
|
+
1 context, 1 specification, 0 failures
|
177
|
+
</pre>
|
178
|
+
|
179
|
+
... and then run all the specs ...
|
180
|
+
|
181
|
+
<pre>
|
182
|
+
$ spec stack_spec.rb -f s
|
183
|
+
|
184
|
+
A new stack
|
185
|
+
- should be empty
|
186
|
+
|
187
|
+
An empty stack
|
188
|
+
- should keep its mouth shut when you send it 'push'
|
189
|
+
- should not be empty after 'push'
|
190
|
+
|
191
|
+
A stack with one item
|
192
|
+
- should return top when you send it 'top'
|
193
|
+
- should not be empty after 'top'
|
194
|
+
|
195
|
+
Finished in 0.000859 seconds
|
196
|
+
|
197
|
+
3 contexts, 5 specifications, 0 failures
|
198
|
+
</pre>
|
199
|
+
|
200
|
+
... and we can press on with confidence that the last spec we added is really exercising and setting expectations on the code we want it to.
|
201
|
+
|
202
|
+
<a href="stack_03.html">Previous</a> |
|
203
|
+
<a href="stack_05.html">Next</a>
|
@@ -0,0 +1,123 @@
|
|
1
|
+
h2. A Simple Stack - Top - IN PROGRESS - DISREGARD THIS PAGE
|
2
|
+
|
3
|
+
What should happen when you call top on an empty stack? We could just return nil,
|
4
|
+
but how does the client know whether the stack was empty or contained nil as an element?
|
5
|
+
Perhaps the client doesn't care. Let's assume the client does care, in which case we'd want to raise an error.
|
6
|
+
|
7
|
+
We already have our "empty stack" context, so let's add a specification to it.
|
8
|
+
|
9
|
+
<ruby>
|
10
|
+
context "An empty stack" do
|
11
|
+
|
12
|
+
setup do
|
13
|
+
@stack = Stack.new
|
14
|
+
end
|
15
|
+
|
16
|
+
specify "should keep its mouth shut when you send it 'push'" do
|
17
|
+
lambda { @stack.push Object.new }.should.not.raise Exception
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "should raise a StackUnderflowError when you send it 'top'" do
|
21
|
+
lambda { @stack.top }.should.raise StackUnderflowError
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
</ruby>
|
26
|
+
|
27
|
+
Running the spec...
|
28
|
+
|
29
|
+
<pre>
|
30
|
+
$ spec stack_spec.rb -v
|
31
|
+
|
32
|
+
An empty stack
|
33
|
+
- should keep its mouth shut when you send it 'push'
|
34
|
+
- should raise a StackUnderflowError when you send it 'top' (FAILED - 1)
|
35
|
+
|
36
|
+
A stack with one item
|
37
|
+
- should return top when you send it 'top'
|
38
|
+
|
39
|
+
|
40
|
+
1)
|
41
|
+
NameError in 'An empty stack should raise a StackUnderflowError when you send it 'top''
|
42
|
+
uninitialized constant StackUnderflowError
|
43
|
+
./stack_spec.rb:14:in `should raise a StackUnderflowError when you send it 'top''
|
44
|
+
|
45
|
+
Finished in 0.000582 seconds
|
46
|
+
|
47
|
+
2 contexts, 3 specifications, 1 failure
|
48
|
+
</pre>
|
49
|
+
|
50
|
+
"uninitialized constant StackUnderflowError" tells us that we need to create a StackUndeflowError. We'll just stick that in the file with the Stack:
|
51
|
+
|
52
|
+
<ruby>
|
53
|
+
class StackUnderflowError < RuntimeError
|
54
|
+
end
|
55
|
+
</ruby>
|
56
|
+
|
57
|
+
and run the spec again.
|
58
|
+
|
59
|
+
<pre>
|
60
|
+
$ spec stack_spec.rb -v
|
61
|
+
|
62
|
+
An empty stack
|
63
|
+
- should keep its mouth shut when you send it 'push'
|
64
|
+
- should raise a StackUnderflowError when you send it 'top' (FAILED - 1)
|
65
|
+
|
66
|
+
A stack with one item
|
67
|
+
- should return top when you send it 'top'
|
68
|
+
|
69
|
+
|
70
|
+
1)
|
71
|
+
ExpectationNotMetError in 'An empty stack should raise a StackUnderflowError when you send it 'top''
|
72
|
+
<Proc> should raise <StackUnderflowError> but raised nothing
|
73
|
+
./stack_spec.rb:14:in `should raise a StackUnderflowError when you send it 'top''
|
74
|
+
|
75
|
+
Finished in 0.000788 seconds
|
76
|
+
|
77
|
+
2 contexts, 3 specifications, 1 failure
|
78
|
+
</pre>
|
79
|
+
|
80
|
+
Now the error report tells us that the expectation was not met - executing the block did not
|
81
|
+
raise the error we specified. Now we can implement code to meet this specification. Sticking
|
82
|
+
with the principle that we want to implement the least amount of code to meet the specification,
|
83
|
+
we enhance our top method:
|
84
|
+
|
85
|
+
<ruby>
|
86
|
+
def top
|
87
|
+
raise StackUnderflowError if @item.nil?
|
88
|
+
@item
|
89
|
+
end
|
90
|
+
</ruby>
|
91
|
+
|
92
|
+
run the specs and they all pass.
|
93
|
+
|
94
|
+
<pre>
|
95
|
+
$ spec stack_spec.rb -v
|
96
|
+
|
97
|
+
An empty stack
|
98
|
+
- should keep its mouth shut when you send it 'push'
|
99
|
+
- should raise a StackUnderflowError when you send it 'top'
|
100
|
+
|
101
|
+
A stack with one item
|
102
|
+
- should return top when you send it 'top'
|
103
|
+
|
104
|
+
|
105
|
+
Finished in 0.000557 seconds
|
106
|
+
|
107
|
+
2 contexts, 3 specifications, 0 failures
|
108
|
+
</pre>
|
109
|
+
|
110
|
+
Do you see how the verbose output is looking more and more like a specification? That's pretty nice, but as your projects grow, you may not want all of that output all of the time. You can, if you wish, run the specs without the <code>-v</code> flag, in which case you get a "." for every passing spec and an "F" for every failing spec. Run them now and you'll see nothing but dots.
|
111
|
+
|
112
|
+
<pre>
|
113
|
+
$ spec stack_spec.rb
|
114
|
+
|
115
|
+
...
|
116
|
+
|
117
|
+
Finished in 0.000458 seconds
|
118
|
+
|
119
|
+
2 contexts, 3 specifications, 0 failures
|
120
|
+
</pre>
|
121
|
+
|
122
|
+
<a href="stack_02.html">Previous</a> |
|
123
|
+
<a href="stack_04.html">Next</a>
|
@@ -0,0 +1,90 @@
|
|
1
|
+
h2. A Simple Stack - Moving specs between contexts
|
2
|
+
|
3
|
+
So we now have the following spec.
|
4
|
+
|
5
|
+
<pre>
|
6
|
+
$ spec stack_spec.rb -f s
|
7
|
+
|
8
|
+
A new stack
|
9
|
+
- should be empty
|
10
|
+
|
11
|
+
An empty stack
|
12
|
+
- should keep its mouth shut when you send it 'push'
|
13
|
+
- should not be empty after 'push'
|
14
|
+
|
15
|
+
A stack with one item
|
16
|
+
- should return top when you send it 'top'
|
17
|
+
- should not be empty after 'top'
|
18
|
+
|
19
|
+
Finished in 0.000882 seconds
|
20
|
+
|
21
|
+
3 contexts, 5 specifications, 0 failures
|
22
|
+
</pre>
|
23
|
+
|
24
|
+
One thing that's odd about this is that "An empty stack should not be empty after 'push'". If you think about it, "An empty stack" is no longer empty after sending it 'push'. It's now "A stack with one item". Conveniently, we already have a context that expresses that. So it may make more sense to move that spec to the "one item" context. Remove it from "An empty stack" ...
|
25
|
+
|
26
|
+
<ruby>
|
27
|
+
context "An empty stack" do
|
28
|
+
specify "should not be empty after 'push'" do
|
29
|
+
@stack.push 37
|
30
|
+
@stack.should_not_be_empty
|
31
|
+
end
|
32
|
+
end
|
33
|
+
</ruby>
|
34
|
+
|
35
|
+
... add it to "A stack with one item" ...
|
36
|
+
|
37
|
+
<ruby>
|
38
|
+
context "A stack with one item" do
|
39
|
+
setup do
|
40
|
+
@stack = Stack.new
|
41
|
+
@stack.push "one item"
|
42
|
+
end
|
43
|
+
|
44
|
+
specify "should not be empty after 'push'" do
|
45
|
+
@stack.push 37
|
46
|
+
@stack.should_not_be_empty
|
47
|
+
end
|
48
|
+
|
49
|
+
...
|
50
|
+
end
|
51
|
+
</ruby>
|
52
|
+
|
53
|
+
... and adjust so it works better in this context. We're already pushing "one item", so we don't have to push 37. And we can adjust the name as well:
|
54
|
+
|
55
|
+
<ruby>
|
56
|
+
context "A stack with one item" do
|
57
|
+
setup do
|
58
|
+
@stack = Stack.new
|
59
|
+
@stack.push "one item"
|
60
|
+
end
|
61
|
+
|
62
|
+
specify "should not be empty" do
|
63
|
+
@stack.should_not_be_empty
|
64
|
+
end
|
65
|
+
...
|
66
|
+
end
|
67
|
+
</ruby>
|
68
|
+
<br>
|
69
|
+
<pre>
|
70
|
+
$ spec stack_spec.rb -f s
|
71
|
+
|
72
|
+
A new stack
|
73
|
+
- should be empty
|
74
|
+
|
75
|
+
An empty stack
|
76
|
+
- should keep its mouth shut when you send it 'push'
|
77
|
+
|
78
|
+
A stack with one item
|
79
|
+
- should not be empty
|
80
|
+
- should return top when you send it 'top'
|
81
|
+
- should not be empty after 'top'
|
82
|
+
|
83
|
+
Finished in 0.000838 seconds
|
84
|
+
|
85
|
+
3 contexts, 5 specifications, 0 failures
|
86
|
+
</pre>
|
87
|
+
|
88
|
+
So keep your eye out for misplaced specifications like this. Feel free to move things around - carefully, of course. It may seem trivial at this point, but as your spec grows it's going to become more and more important. These specifications are documentation of your system. They must be clear, well organized, and readable.
|
89
|
+
|
90
|
+
<a href="stack_04.html">Previous</a>
|
@@ -0,0 +1,124 @@
|
|
1
|
+
h2. A Simple Stack - Organization and Documentation - IN PROGRESS - DISREGARD THIS PAGE
|
2
|
+
|
3
|
+
h3. Spec organization
|
4
|
+
|
5
|
+
Let's take a look at the spec thus far...
|
6
|
+
|
7
|
+
<ruby>
|
8
|
+
require File.dirname(__FILE__) + "/stack"
|
9
|
+
|
10
|
+
context "An empty stack" do
|
11
|
+
|
12
|
+
setup do
|
13
|
+
@stack = Stack.new
|
14
|
+
end
|
15
|
+
|
16
|
+
specify "should keep its mouth shut when you send it 'push'" do
|
17
|
+
lambda { @stack.push Object.new }.should.not.raise Exception
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "should raise a StackUnderflowError when you send it 'top'" do
|
21
|
+
lambda { @stack.top }.should.raise StackUnderflowError
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
context "A stack with one item" do
|
27
|
+
|
28
|
+
setup do
|
29
|
+
@stack = Stack.new
|
30
|
+
@stack.push "one item"
|
31
|
+
end
|
32
|
+
|
33
|
+
specify "should return top when sent the 'top' message" do
|
34
|
+
@stack.top.should.equal "one item"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
</ruby>
|
39
|
+
|
40
|
+
..and the result of executing them. First in default mode...
|
41
|
+
|
42
|
+
<pre>
|
43
|
+
$ spec stack_spec.rb
|
44
|
+
|
45
|
+
...
|
46
|
+
|
47
|
+
Finished in 0.000439 seconds
|
48
|
+
|
49
|
+
2 contexts, 3 specifications, 0 failures
|
50
|
+
</pre>
|
51
|
+
|
52
|
+
...and then in verbose mode...
|
53
|
+
|
54
|
+
<pre>
|
55
|
+
$ spec stack_spec.rb -v
|
56
|
+
|
57
|
+
An empty stack
|
58
|
+
- should keep its mouth shut when you send it 'push'
|
59
|
+
- should raise a StackUnderflowError when you send it 'top'
|
60
|
+
|
61
|
+
A stack with one item
|
62
|
+
- should return top when you send it 'top'
|
63
|
+
|
64
|
+
|
65
|
+
Finished in 0.000533 seconds
|
66
|
+
|
67
|
+
2 contexts, 3 specifications, 0 failures
|
68
|
+
</pre>
|
69
|
+
|
70
|
+
We've got two contexts. In both we exercise the 'top' message, but we only exercise 'push' in one. This is a wonderful aspect of organizing the specs this way. By looking at the contexts themselves, or by looking at the generated output, and by having only one setup for each context (a guideline often suggested for xUnit, but violated just as often), we can clearly see the imbalance in what messages we're specifying in the different contexts.
|
71
|
+
|
72
|
+
So now, in addition to using the principle of the simplest thing to help us decide what to specify next, we've also got this feedback that tells us that we have yet to specify how a stack with one item should respond to the 'push' message. Using both tools to guide us, that is an obvious next choice.
|
73
|
+
|
74
|
+
<ruby>
|
75
|
+
context "A stack with one item" do
|
76
|
+
|
77
|
+
setup do
|
78
|
+
@stack = Stack.new
|
79
|
+
@stack.push "one item"
|
80
|
+
end
|
81
|
+
|
82
|
+
specify "should keep its mouth shut when you send it 'push'" do
|
83
|
+
lambda { @stack.push Object.new }.should.not.raise Exception
|
84
|
+
end
|
85
|
+
|
86
|
+
specify "should return top when you send it 'top'" do
|
87
|
+
@stack.top.should.equal "one item"
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
</ruby>
|
92
|
+
|
93
|
+
To add that, I actually copied the spec from the "new stack" context. Cut and paste? Blasphemy! Exact duplication? More blasphemy! Well, it is blasphemy if you think of these structures as tests, or even as code. Yes, they are executable. Yes, they are code - sort of. But they also serve other very important purpose - they are documentation.
|
94
|
+
|
95
|
+
h3. Specs as documentation
|
96
|
+
|
97
|
+
The whole structure of contexts and specifications was deliberately chosen to feel less like the tests that we're all used to seeing in xUnit frameworks so that we wouldn't think of specs in the same way as we do tests. So while we might think of them as executable, we don't want to think of them as code.
|
98
|
+
|
99
|
+
We talk about tests as documentation in TDD as well. But I can tell you that when I've tried to read them as such and found myself searching through hierarchies to find the setup for a test that was failing, my eyes have just glazed over and I've ended up looking directly at the code. Serving as documentation requires of these executable specifications that they be simple, clear and easy to understand. All the context you need to understand the test should be in one place.
|
100
|
+
|
101
|
+
Also, the perception that "all duplication is evil" is based on industry-wide experience in which changes that needed to happen to duplicated code didn't happen everywhere it should have. In the case of specifications, that doesn't really fly because each specification is autonomous. If the rules change for a message in a given context, then you'd only want to make the change in that context.
|
102
|
+
|
103
|
+
So we've now got 4 specifications, 2 each in 2 contexts. Run the specs...
|
104
|
+
|
105
|
+
<pre>$ spec stack_spec.rb -v
|
106
|
+
|
107
|
+
An empty stack
|
108
|
+
- should keep its mouth shut when you send it 'push'
|
109
|
+
- should raise a StackUnderflowError when you send it 'top'
|
110
|
+
|
111
|
+
A stack with one item
|
112
|
+
- should keep its mouth shut when you send it 'push'
|
113
|
+
- should return top when you send it 'top'
|
114
|
+
|
115
|
+
|
116
|
+
Finished in 0.000657 seconds
|
117
|
+
|
118
|
+
2 contexts, 4 specifications, 0 failures
|
119
|
+
</pre>
|
120
|
+
|
121
|
+
...and there are no failures. So in this case, since 'push' results in the same behavior for an empty stack and a one-item stack, we don't have anything additional to implement.
|
122
|
+
|
123
|
+
<a href="stack_03.html">Previous</a> |
|
124
|
+
<a href="stack_05.html">Next</a>
|