-
Notifications
You must be signed in to change notification settings - Fork 0
/
05.3.txt
199 lines (139 loc) · 6.9 KB
/
05.3.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
5.3 Green: Get the Example to Pass
The failure message tells us that output never received puts. Here’s what we need to do to get this example to pass:
Download cb/10/lib/codebreaker/game.rb
module Codebreaker
class Game
def initialize(output)
@output = output
end
def start
@output.puts 'Welcome to Codebreaker!'
end
end
end
Make those changes and run the rspec command again, and you should see this:
Codebreaker::Game
#start
sends a welcome message
prompts for the first guess (PENDING: Not Yet Implemented)
Pending:
Codebreaker::Game#start prompts for the first guess
# Not Yet Implemented
# ./spec/codebreaker/game_spec.rb:14
Finished in 0.00144 seconds
2 examples, 0 failures, 1 pending
We have our first passing example! We’ve gone from red to green. The next step in the cycle is to refactor. We don’t really have any duplication yet, so let’s see whether we’ve had any impact on the features:
Scenario: start game
Given I am not yet playing
When I start a new game
Then I should see "Welcome to Codebreaker!"
And I should see "Enter guess:"
expected ["Welcome to Codebreaker!"] to include "Enter guess:"
Progress! Now one of the two Thens is passing, so it looks like we’re about halfway done with this feature. Actually, we’re quite a bit more than halfway done, because, as you’ll soon see, all the pieces are al- ready in place for the rest.
Next Step
The following failing step is the next thing to work on: And I should see “Enter guess:”. Go ahead and add an example for this behavior to game_spec.rb:
Download cb/11/spec/codebreaker/game_spec.rb
require 'spec_helper'
module Codebreaker
describe Game do
describe "#start" do
it "sends a welcome message" do
output = double('output')
game = Game.new(output)
output.should_receive(:puts).with('Welcome to Codebreaker!')
game.start
end
it "prompts for the first guess" do
output = double('output')
game = Game.new(output)
output.should_receive(:puts).with('Enter guess:')
game.start
end
end
end
end
This is very similar to the first example, but we’re expecting a different message. We’ll come back and DRY that up in a bit, but first let’s get it passing. Run the spec, and watch it fail:
Codebreaker::Game
#start
sends a welcome message
prompts for the first guess (FAILED - 1)
Failures:
1) Codebreaker::Game#start prompts for the first guess
Failure/Error: game.start
Double "output" received :puts with unexpected arguments
expected: ("Enter guess:")
got: ("Welcome to Codebreaker!")
# ./lib/codebreaker/game.rb:8:in `start'
# ./spec/codebreaker/game_spec.rb:22
Finished in 0.00199 seconds
2 examples, 1 failure
This time, the output didn’t receive puts('Enter guess:'). Resolve that as follows:
Download cb/12/lib/codebreaker/game.rb
module Codebreaker
class Game
def initialize(output)
@output = output
end
def start
@output.puts 'Welcome to Codebreaker!'
@output.puts 'Enter guess:'
end
end
end
Run the rspec command:
Codebreaker::Game
#start
sends a welcome message (FAILED - 1)
prompts for the first guess (FAILED - 2)
Failures:
1) Codebreaker::Game#start sends a welcome message
Failure/Error: game.start
Double "output" received :puts with unexpected arguments
expected: ("Welcome to Codebreaker!")
got: ("Enter guess:")
# ./lib/codebreaker/game.rb:10:in `start'
# ./spec/codebreaker/game_spec.rb:12
2) Codebreaker::Game#start prompts for the first guess
Failure/Error: game.start
Double "output" received :puts with unexpected arguments
expected: ("Enter guess:")
got: ("Welcome to Codebreaker!")
# ./lib/codebreaker/game.rb:8:in `start'
# ./spec/codebreaker/game_spec.rb:21
Finished in 0.00219 seconds
2 examples, 2 failures
And ta-da! Now not only is the second example still failing, but the first example is failing as well! Who’da thunk? This may seem a bit confusing if you’ve never worked with test doubles and message expectations before, but test doubles are like computers. They are extraordinarily obedient, but they are not all that clever. By default, they will expect exactly what you tell them to expect, nothing more and nothing less.
We’ve told the double in the first example to expect puts( ) with “Welcome to Codebreaker!” and we’ve satisfied that requirement, but we’ve only told it to expect “Welcome to Codebreaker!” It doesn’t know anything about “Enter guess:”
Similarly, the double in the second example expects “Enter guess:” but the first message it gets is “Welcome to Codebreaker!”
We could combine these two into a single example, but we like to follow the guideline of “one expectation per example.” The rationale here is that if there are two expectations in an example that should both fail given the implementation at that moment, we’ll only see the first failure. No sooner do we meet that expectation than we discover that we haven’t met the second expectation. If they live in separate examples, then they’ll both fail, and that will provide us with more accurate
information than if only one of them is failing.
We could also try to break the messages up into different steps, but we’ve already defined how we want to talk to the game object. So, how can we resolve this?
as_null_object
There are a couple of ways we can go about it, but the simplest way is to tell the double output to only listen for the messages we tell it to expect and ignore any other messages.3 This is based on the Null Object design pattern described in Pattern Languages of Program Design 3 [MRB97] and is supported by RSpec’s double framework with the as_null_object( ) method:
Download cb/13/spec/codebreaker/game_spec.rb
require 'spec_helper'
module Codebreaker describe
Game do
describe "#start" do
it "sends a welcome message" do
output = double('output').as_null_object game = Game.new(output)
output.should_receive(:puts).with('Welcome to Codebreaker!')
game.start
end
it "prompts for the first guess" do
output = double('output').as_null_object game = Game.new(output)
output.should_receive(:puts).with('Enter guess:')
game.start
end
end
end
end
3. Actually, that’s not completely true. Unexpected messages are actually recorded because it is sometimes helpful to include them in failure messages.
Run the rspec command again, and you should see this:
Codebreaker::Game
#start
sends a welcome message
prompts for the first guess
Finished in 0.00174 seconds
2 examples, 0 failures
Good news. Both examples are now passing. Now that we have green, it’s time to refactor!