lookout 2.1.4 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,7 +1,7 @@
1
- # Lookout #
1
+ Lookout
2
2
 
3
- Lookout is a lightweight unit testing framework. Tests (expectations) can be
4
- written as follows
3
+ Lookout is a lightweight unit testing framework. Tests (expectations) can be
4
+ written as follows
5
5
 
6
6
  expect 2 do
7
7
  1 + 1
@@ -11,635 +11,651 @@ written as follows
11
11
  Object.invalid_method_call
12
12
  end
13
13
 
14
- Lookout is designed to encourage – force, even – unit testing best practices
15
- such as
14
+ Lookout is designed to encourage – force, even – unit testing best practices
15
+ such as
16
16
 
17
- * Setting up only one expectation per test
18
- * Not setting expectations on non-public APIs
19
- * Test isolation
17
+ Setting up only one expectation per test
18
+ Not setting expectations on non-public APIs
19
+ Test isolation
20
20
 
21
- This is done by
21
+ This is done by
22
22
 
23
- * Only allowing one expectation to be set per test
24
- * Providing no (additonal) way of accessing private state
25
- * Providing no setup and teardown methods, nor a method of providing test
23
+ Only allowing one expectation to be set per test
24
+ Providing no (additonal) way of accessing private state
25
+ Providing no setup and teardown methods, nor a method of providing test
26
26
  helpers
27
27
 
28
- Other important points are
28
+ Other important points are
29
29
 
30
- * A unified syntax for setting up both state-based and behavior-based
30
+ A unified syntax for setting up both state-based and behavior-based
31
31
  expectations
32
- * A focus on code readability by providing no mechanism for describing an
32
+ A focus on code readability by providing no mechanism for describing an
33
33
  expectation other than the code in the expectation itself
34
34
 
35
- The way Lookout works has been heavily influenced by [expectations], by [Jay
36
- Fields][]. The code base was once also heavily based on [expectations],
37
- based at Subversion [revision r76][]. A lot has happened since then and all of the
38
- work past that revision are due to [Nikolai Weibull][].
35
+ The way Lookout works has been heavily influenced by expectations¹, by
36
+ {Jay Fields}². The code base was once also heavily based on expectations¹,
37
+ based at Subversion {revision 76}³. A lot has happened since then and all of
38
+ the work past that revision are due to {Nikolai Weibull}⁴.
39
39
 
40
- [expectations]: http://expectations.rubyforge.org
41
- [Jay Fields]: http://blog.jayfields.com
42
- [revision r76]: https://github.com/now/lookout/commit/537bedf3e5b3eb4b31c066b3266f42964ac35ebe
43
- [Nikolai Weibull]: http://bitwi.se
40
+ ¹ Get more information on the expectations library at http://expectations.rubyforge.org/
41
+ ² Read Jay Fields’s blog at http://blog.jayfields.com/
42
+ ³ Review Lookout revision 76 at https://github.com/now/lookout/commit/537bedf3e5b3eb4b31c066b3266f42964ac35ebe
43
+ ⁴ Visit Nikolai Weibull’s home page at http://bitwi.se/
44
44
 
45
+ § Installation
45
46
 
46
- ## Installation ##
47
+ Install Lookout with
47
48
 
48
- Install Lookout with
49
+ % gem install lookout
49
50
 
50
- % gem install lookout
51
51
 
52
+ § Usage
52
53
 
53
- ## Usage ##
54
+ Lookout allows you to set expectations on an object’s state or behavior.
55
+ We’ll begin by looking at state expectations and then take a look at
56
+ expectations on behavior.
54
57
 
55
- Lookout allows you to set expectations on an object’s state or behavior. We’ll
56
- begin by looking at state expectations and then take a look at expectations on
57
- behavior.
58
+ § Expectations on State
58
59
 
59
- ### Expectations on State ###
60
+ An expectation can be made on the result of a computation:
60
61
 
61
- An expectation can be made on the result of a computation:
62
+ expect 2 do
63
+ 1 + 1
64
+ end
62
65
 
63
- expect 2 do
64
- 1 + 1
65
- end
66
-
67
- Most objects, in fact, have their state expectations checked by invoking `#==`
68
- on the expected value with the result as its argument.
69
-
70
- Checking that a result is within a given range is also simple:
71
-
72
- expect 0.099..0.101 do
73
- 0.4 - 0.3
74
- end
75
-
76
- Here, the more general `#===` is being used on the `Range`.
77
-
78
- `Strings` of course match against `Strings`:
79
-
80
- expect 'ab' do
81
- 'abc'[0..1]
82
- end
66
+ Most objects, in fact, have their state expectations checked by invoking
67
+ ‹#==› on the expected value with the result as its argument.
83
68
 
84
- but we can also match a `String` against a `Regexp`:
69
+ Checking that a result is within a given range is also simple:
85
70
 
86
- expect %r{a substring} do
87
- 'a string with a substring'
88
- end
71
+ expect 0.099..0.101 do
72
+ 0.4 - 0.3
73
+ end
89
74
 
90
- (Please note the use of `%r{…}` to avoid warnings that will be generated when
91
- Ruby parses `expect /…/`.)
75
+ Here, the more general ‹#===› is being used on the ‹Range›.
92
76
 
93
- Checking that the result includes a certain module is done by expecting the
94
- `Module`.
77
+ ‹Strings› of course match against ‹Strings›:
95
78
 
96
- expect Enumerable do
97
- []
98
- end
79
+ expect 'ab' do
80
+ 'abc'[0..1]
81
+ end
99
82
 
100
- This, due to the nature of Ruby, of course also works for classes (as they are
101
- also modules):
83
+ but we can also match a ‹String› against a ‹Regexp›:
102
84
 
103
- expect String do
104
- 'a string'
105
- end
85
+ expect %r{a substring} do
86
+ 'a string with a substring'
87
+ end
106
88
 
107
- This doesn’t hinder us from expecting the actual `Module` itself:
89
+ (Note the use of ‹%r{…}› to avoid warnings that will be generated when
90
+ Ruby parses ‹expect /…/›.)
108
91
 
109
- expect Enumerable do
110
- Enumerable
111
- end
92
+ Checking that the result includes a certain module is done by expecting the
93
+ ‹Module›.
112
94
 
113
- As you may have figured out yourself, this is accomplished by first trying
114
- `#==` and, if it returns `false`, then trying `#===` on the expected `Module`.
115
- This is also true of `Ranges` and `Regexps`.
95
+ expect Enumerable do
96
+ []
97
+ end
116
98
 
117
- Truthfulness is expected with `true` and `false`:
99
+ This, due to the nature of Ruby, of course also works for classes (as
100
+ they are also modules):
118
101
 
119
- expect true do
120
- 1
121
- end
102
+ expect String do
103
+ 'a string'
104
+ end
122
105
 
123
- expect false do
124
- nil
125
- end
126
-
127
- Results equaling `true` or `false` are slightly different:
128
-
129
- expect TrueClass do
130
- true
131
- end
132
-
133
- expect FalseClass do
134
- false
135
- end
106
+ This doesn’t hinder us from expecting the actual ‹Module› itself:
136
107
 
137
- The rationale for this is that you should only care if the result of a
138
- computation evaluates to a value that Ruby considers to be either true or
139
- false, not the exact literals `true` or `false`.
108
+ expect Enumerable do
109
+ Enumerable
110
+ end
140
111
 
141
- Expecting output on an IO object is also common:
112
+ As you may have figured out yourself, this is accomplished by first
113
+ trying ‹#==› and, if it returns ‹false›, then trying ‹#===› on the
114
+ expected ‹Module›. This is also true of ‹Ranges› and ‹Regexps›.
142
115
 
143
- expect output("abc\ndef\n") do |io|
144
- io.puts 'abc', 'def'
145
- end
116
+ Truthfulness is expected with ‹true› and ‹false›:
146
117
 
147
- This can be used to capture the output of a formatter that takes an output
148
- object as a parameter.
118
+ expect true do
119
+ 1
120
+ end
149
121
 
150
- You should always be expecting errors from – and in, but that’s a different
151
- story – your code:
122
+ expect false do
123
+ nil
124
+ end
152
125
 
153
- expect NoMethodError do
154
- Object.no_method
155
- end
126
+ Results equaling ‹true› or ‹false› are slightly different:
156
127
 
157
- Often, not only the type of the error, but its description, is important to
158
- check:
128
+ expect TrueClass do
129
+ true
130
+ end
159
131
 
160
- expect StandardError.new('message') do
161
- raise StandardError.new('message')
162
- end
132
+ expect FalseClass do
133
+ false
134
+ end
163
135
 
164
- As with `Strings`, `Regexps` can be used to check the error description:
136
+ The rationale for this is that you should only care if the result of a
137
+ computation evaluates to a value that Ruby considers to be either true or
138
+ false, not the exact literals ‹true› or ‹false›.
165
139
 
166
- expect StandardError.new(/mess/) do
167
- raise StandardError.new('message')
168
- end
140
+ Expecting output on an IO object is also common:
169
141
 
170
- (Note that some of Ruby’s built-in error classes have slightly complicated
171
- behavior and will not allow you to pass a `Regexp` as a parameter. `NameError`
172
- is such a class. This may warrant further investigation into whether or not
173
- this is a bug, but I’ll leave that up to the reader to decide.)
142
+ expect output("abc\ndef\n") do |io|
143
+ io.puts 'abc', 'def'
144
+ end
174
145
 
175
- Lookout further provides a fluent way of setting up expectations on boolean
176
- results. An object can “be”
146
+ This can be used to capture the output of a formatter that takes an
147
+ output object as a parameter.
177
148
 
178
- expect Class.new{ attr_accessor :running; }.new.to.be.running do |process|
179
- process.running = true
180
- end
149
+ You should always be expecting errors from – and in, but that’s a
150
+ different story – your code:
181
151
 
182
- or “not be”
152
+ expect NoMethodError do
153
+ Object.no_method
154
+ end
183
155
 
184
- expect Class.new{ attr_accessor :running; }.new.not.to.be.running do |process|
185
- process.running = false
186
- end
156
+ Often, not only the type of the error, but its description, is important
157
+ to check:
187
158
 
188
- or to “have”
159
+ expect StandardError.new('message') do
160
+ raise StandardError.new('message')
161
+ end
189
162
 
190
- expect Class.new{ attr_accessor :finished; }.new.to.have.finished do |process|
191
- process.finished = true
192
- end
163
+ As with ‹Strings›, ‹Regexps› can be used to check the error description:
193
164
 
194
- or “not have”
165
+ expect StandardError.new(/mess/) do
166
+ raise StandardError.new('message')
167
+ end
195
168
 
196
- expect Class.new{ attr_accessor :finished; }.new.not.to.have.finished do |process|
197
- process.finished = false
198
- end
169
+ (Note that some of Ruby’s built-in error classes have slightly
170
+ complicated behavior and will not allow you to pass a ‹Regexp› as a
171
+ parameter. ‹NameError› is such a class. This may warrant further
172
+ investigation into whether or not this is a bug, but I’ll leave that up
173
+ to the reader to decide.)
199
174
 
200
- On the same note
175
+ Lookout further provides a fluent way of setting up expectations on
176
+ boolean results. An object can “be”
201
177
 
202
- expect nil.to.be.nil?
178
+ expect Class.new{ attr_accessor :running; }.new.to.be.running do |process|
179
+ process.running = true
180
+ end
203
181
 
204
- and
182
+ or “not be”
205
183
 
206
- expect Object.new.not.to.be.nil?
184
+ expect Class.new{ attr_accessor :running; }.new.not.to.be.running do |process|
185
+ process.running = false
186
+ end
207
187
 
208
- As not every boolean method is or “has” you can even
188
+ or tohave
209
189
 
210
- expect nil.to.respond_to? :nil?
190
+ expect Class.new{ attr_accessor :finished; }.new.to.have.finished do |process|
191
+ process.finished = true
192
+ end
211
193
 
212
- The rules here are that all `Objects` respond to `#to`. After `#to` you may
213
- call
194
+ or “not have”
214
195
 
215
- * `#not`
216
- * `#be`
217
- * `#have`
218
- * Any method whose name ends with `?`
196
+ expect Class.new{ attr_accessor :finished; }.new.not.to.have.finished do |process|
197
+ process.finished = false
198
+ end
219
199
 
220
- A call to `#not` must be followed by a call to one of the three alternatives
221
- that follow it in the list. `#Be` and `#Have` must be followed by a call to a
222
- method.
200
+ On the same note
223
201
 
224
- ### Expectations on Behavior ###
202
+ expect nil.to.be.nil?
225
203
 
226
- We expect our objects to be on their best behavior. Lookout allows you to make
227
- sure that they are.
204
+ and
228
205
 
229
- Mocks let use verify that a method is called in the way that we expect it to
230
- be:
206
+ expect Object.new.not.to.be.nil?
231
207
 
232
- expect mock.to.receive.dial('2125551212').twice do |phone|
233
- phone.dial('2125551212')
234
- phone.dial('2125551212')
235
- end
208
+ As not every boolean method “is” or “has” you can even
236
209
 
237
- Here, `#mock` creates a mock object, an object that doesn’t respond to
238
- anything unless you tell it to. We tell it to expect to receive a call to
239
- `#dail` with `'2125551212'` as its formal argument, and we expect it to receive
240
- it twice. The mock object is then passed in to the block so that the
241
- expectations placed upon it can be fulfilled.
242
-
243
- Sometimes we only want to make sure that a method is called in the way that we
244
- expect it to be, but we don’t care if any other methods are called on the
245
- object. A stub object, created with `#stub`, expects any method and returns a
246
- stub object that, again, expects any method, and thus fits the bill.
247
-
248
- expect stub.to.receive.dial('2125551212').twice do |phone|
249
- phone.dial('2125551212')
250
- phone.hangup
251
- phone.dial('2125551212')
252
- end
210
+ expect nil.to.respond_to? :nil?
253
211
 
254
- We can also use stubs without any expectations on them:
212
+ The rules here are that all ‹Objects› respond to ‹#to›. After ‹#to› you
213
+ may call
255
214
 
256
- expect 3 do
257
- s = stub(:a => 1, :b => 2)
258
- s.a + s.b
259
- end
215
+ • ‹#not›
216
+ • ‹#be›
217
+ • ‹#have›
218
+ • Any method whose name ends with ‹?›
260
219
 
261
- and we can stub out a specific method on an object:
220
+ A call to ‹#not› must be followed by a call to one of the three
221
+ alternatives that follow it in the list. ‹#Be› and ‹#have› must be
222
+ followed by a call to a method.
262
223
 
263
- expect 'def' do
264
- a = 'abc'
265
- stub(a).to_str{ 'def' }
266
- a.to_str
267
- end
224
+ § Expectations on Behavior
268
225
 
269
- You don’t have to use a mock object to verify that a method is called:
226
+ We expect our objects to be on their best behavior. Lookout allows you
227
+ to make sure that they are.
270
228
 
271
- expect Object.to.receive.deal do
272
- Object.deal
273
- end
229
+ Mocks let use verify that a method is called in the way that we expect it
230
+ to be:
274
231
 
275
- As you have figured out by now, the expected method call is set up by calling
276
- `#receive` after `#to`. `#Receive` is followed by a call to the method to
277
- expect with any expected arguments. The body of the mocked method can be given
278
- as the block to the method. Finally, an expected invocation count may follow
279
- the method. Let’s look at this formal specification in more detail.
232
+ expect mock.to.receive.dial('2125551212').twice do |phone|
233
+ phone.dial('2125551212')
234
+ phone.dial('2125551212')
235
+ end
280
236
 
281
- The expected method arguments may be given in a variety of ways. Let’s
282
- introduce them by giving some examples:
237
+ Here, ‹#mock› creates a mock object, an object that doesn’t respond to
238
+ anything unless you tell it to. We tell it to expect to receive a call
239
+ to ‹#dail› with ‹'2125551212'› as its formal argument, and we expect it
240
+ to receive it twice. The mock object is then passed in to the block so
241
+ that the expectations placed upon it can be fulfilled.
283
242
 
284
- expect mock.to.receive.a do |m|
285
-
286
- end
243
+ Sometimes we only want to make sure that a method is called in the way
244
+ that we expect it to be, but we don’t care if any other methods are
245
+ called on the object. A stub object, created with ‹#stub›, expects any
246
+ method and returns a stub object that, again, expects any method, and
247
+ thus fits the bill.
287
248
 
288
- Here, the method `#a` must be called with any number of arguments. It may be
289
- called any number of times, but it must be called at least once.
249
+ expect stub.to.receive.dial('2125551212').twice do |phone|
250
+ phone.dial('2125551212')
251
+ phone.hangup
252
+ phone.dial('2125551212')
253
+ end
290
254
 
291
- If a method must receive exactly one argument, you can use `arg`:
255
+ We can also use stubs without any expectations on them:
292
256
 
293
- expect mock.to.receive.a(arg) do |m|
294
-
295
- end
257
+ expect 3 do
258
+ s = stub(:a => 1, :b => 2)
259
+ s.a + s.b
260
+ end
296
261
 
297
- If a method must receive a specific argument, you can use that argument:
262
+ and we can stub out a specific method on an object:
298
263
 
299
- expect mock.to.receive.a(1..2) do |m|
300
-
301
- end
264
+ expect 'def' do
265
+ a = 'abc'
266
+ stub(a).to_str{ 'def' }
267
+ a.to_str
268
+ end
302
269
 
303
- The same matching rules apply for arguments as they do for state expectations,
304
- so the previous example expects a call to `#a` with 1, 2, or the Range 1..2 as
305
- an argument on `m`.
270
+ You don’t have to use a mock object to verify that a method is called:
306
271
 
307
- If a method must be invoked without any arguments you can use
308
- `without_arguments`:
272
+ expect Object.to.receive.deal do
273
+ Object.deal
274
+ end
309
275
 
310
- expect mock.to.receive.a(without_arguments) do |m|
311
-
312
- end
276
+ As you have figured out by now, the expected method call is set up by
277
+ calling ‹#receive› after ‹#to›. ‹#Receive› is followed by a call to the
278
+ method to expect with any expected arguments. The body of the mocked
279
+ method can be given as the block to the method. Finally, an expected
280
+ invocation count may follow the method. Let’s look at this formal
281
+ specification in more detail.
313
282
 
314
- You can of course use both `arg` and actual arguments:
283
+ The expected method arguments may be given in a variety of ways. Let’s
284
+ introduce them by giving some examples:
315
285
 
316
- expect mock.to.receive.a(arg, 1, arg) do |m|
317
-
318
- end
286
+ expect mock.to.receive.a do |m|
287
+
288
+ end
319
289
 
320
- The body of the mock method may be given as the block. Here, calling `#a` on
321
- `m` will give the result `1`:
290
+ Here, the method ‹#a› must be called with any number of arguments. It
291
+ may be called any number of times, but it must be called at least once.
322
292
 
323
- expect mock.to.receive.a{ 1 } do |m|
324
-
325
- end
293
+ If a method must receive exactly one argument, you can use ‹arg›:
326
294
 
327
- If no body has been given, the result will be a stub object.
295
+ expect mock.to.receive.a(arg) do |m|
296
+
297
+ end
328
298
 
329
- There is a caveat here in that a block can’t yield in Ruby 1.8. To work around
330
- this deficiency you have to use the `#yield` method:
299
+ If a method must receive a specific argument, you can use that argument:
331
300
 
332
- expect mock.to.receive.a.yield(1) do |m|
333
-
334
- end
301
+ expect mock.to.receive.a(1..2) do |m|
302
+
303
+ end
335
304
 
336
- Any number of values to yield upon successive calls may be given. The last
337
- value given will be used repeatedly when all preceding values have been
338
- consumed. It’s also important to know that values are splatted when they are
339
- yielded.
305
+ The same matching rules apply for arguments as they do for state
306
+ expectations, so the previous example expects a call to ‹#a› with 1, 2,
307
+ or the Range 1..2 as an argument on ‹m›.
340
308
 
341
- To simulate an `#each`-like method you can use `#each`. The following horrible
342
- example should give you an idea of how to use it.
309
+ If a method must be invoked without any arguments you can use
310
+ ‹without_arguments›:
343
311
 
344
- expect Object.new.to.receive.each.each(1, 2, 3) do |o|
345
- class << o
346
- include Enumerable
347
- end
348
- o.inject{ |i, a| i + a }
349
- end
312
+ expect mock.to.receive.a(without_arguments) do |m|
313
+
314
+ end
350
315
 
351
- Invocation count expectations can also be set if the default expectation of
352
- “at least once” isn’t good enough. The following expectations are possible
353
-
354
- * `#at_most_once`
355
- * `#once`
356
- * `#at_least_once`
357
- * `#twice`
358
-
359
- And, for a given `N`,
360
-
361
- * `#at_most(N)`
362
- * `#exactly(N)`
363
- * `#at_least(N)`
364
-
365
- Method stubs are another useful thing to have in a unit testing framework.
366
- Sometimes you need to override a method that does something a test shouldn’t
367
- do, like access and alter bank accounts. We can override – stub out – a method
368
- by using the `#stub` method. Let’s assume that we have an `Account` class that
369
- has two methods, `#slips` and `#total`. `#Slips` retrieves the bank slips that
370
- keep track of your deposits to the `Account` from a database. `#Total` sums
371
- the `#slips`. In the following test we want to make sure that `#total` does
372
- what it should do without accessing the database. We therefore stub out
373
- `#slips` and make it return something that we can easily control.
374
-
375
- expect 6 do |m|
376
- account = Account.new
377
- stub(account).slips{ [1, 2, 3] }
378
- account.total
379
- end
316
+ You can of course use both ‹arg› and actual arguments:
380
317
 
381
- As with mock methods, if no body is given, the result will be a stub object.
318
+ expect mock.to.receive.a(arg, 1, arg) do |m|
319
+
320
+ end
382
321
 
383
- To make it easy to create objects with a set of stubbed methods there’s also a
384
- convenience method:
322
+ The body of the mock method may be given as the block. Here, calling
323
+ ‹#a› on ‹m› will give the result ‹1›:
385
324
 
386
- expect 3 do
387
- s = stub(:a => 1, :b => 2)
388
- s.a + s.b
389
- end
325
+ expect mock.to.receive.a{ 1 } do |m|
326
+
327
+ end
390
328
 
391
- Please note that this makes it impossible to stub a method on a Hash, but you
392
- shouldn’t be doing that anyway. In fact, you should never mock or stub methods
393
- on value objects.
329
+ If no body has been given, the result will be a stub object.
394
330
 
395
- ## Integration ##
331
+ There is a caveat here in that a block can’t yield in Ruby 1.8. To work
332
+ around this deficiency you have to use the ‹#yield› method:
396
333
 
397
- Lookout can be used from [Rake][]. Simply include the following code in your
398
- `Rakefile`:
334
+ expect mock.to.receive.a.yield(1) do |m|
335
+
336
+ end
399
337
 
400
- require 'lookout/rake/tasks'
338
+ Any number of values to yield upon successive calls may be given. The
339
+ last value given will be used repeatedly when all preceding values have
340
+ been consumed. It’s also important to know that values are splatted when
341
+ they are yielded.
401
342
 
402
- Lookout::Rake::Tasks::Test.new
343
+ To simulate an ‹#each›-like method you can use ‹#each›. The following
344
+ horrible example should give you an idea of how to use it.
403
345
 
404
- If the `:default` task hasn’t been defined it will be set to depend on the
405
- `:test` task.
346
+ expect Object.new.to.receive.each.each(1, 2, 3) do |o|
347
+ class << o
348
+ include Enumerable
349
+ end
350
+ o.inject{ |i, a| i + a }
351
+ end
406
352
 
407
- As an added bonus you can use Lookouts own [gem][RubyGems] tasks:
353
+ Invocation count expectations can also be set if the default expectation
354
+ of “at least once” isn’t good enough. The following expectations are
355
+ possible
408
356
 
409
- Lookout::Rake::Tasks::Gem.new
357
+ • ‹#at_most_once›
358
+ • ‹#once›
359
+ • ‹#at_least_once›
360
+ • ‹#twice›
410
361
 
411
- This provides tasks to `build`, `check`, `install`, and `push` your gem.
362
+ And, for a given ‹N›,
412
363
 
413
- To use Lookout together with [Vim][], place `contrib/rakelookout.vim` in
414
- `~/.vim/compiler` and add
364
+ • ‹#at_most(N)›
365
+ • ‹#exactly(N)›
366
+ • ‹#at_least(N)›
415
367
 
416
- compiler rakelookout
417
-
418
- to `~/.vim/after/ftplugin/ruby.vim`. Executing `:make` from inside [Vim][]
419
- will now run your tests and an errors and failures can be visited with
420
- `:cnext`. Execute `:help quickfix` for additional information.
421
-
422
- Another useful addition to your `~/.vim/after/ftplugin/ruby.vim` file may be
423
-
424
- nnoremap <buffer> <silent> <Leader>M <Esc>:call <SID>run_test()<CR>
425
- let b:undo_ftplugin .= ' | nunmap <buffer> <Leader>M'
426
-
427
- function! s:run_test()
428
- let test = expand('%')
429
- let line = 'LINE=' . line('.')
430
- if test =~ '^lib/'
431
- let test = substitute(test, '^lib/', 'test/', '')
432
- let line = ""
433
- endif
434
- execute 'make' 'TEST=' . shellescape(test) line
435
- endfunction
436
-
437
- Now, pressing `<Leader>M` will either run all tests for a given class, if the
438
- implementation file is active, or run the test at or just before the cursor, if
439
- the test file is active. This is useful if you’re currently receiving a lot of
440
- errors and/or failures and want to focus on those associated with a specific
441
- class or on a specific test.
442
-
443
- [Rake]: http://rake.rubyforge.org
444
- [RubyGems]: http://rubygems.org
445
-
446
- ## Interface Design ##
447
-
448
- The default output of Lookout can Spartanly be described as Spartan. If no
449
- errors or failures occur, no output is generated. This is unconventional, as
450
- unit testing frameworks tend to dump a lot of information on the user,
451
- concerning things such as progress, test count summaries, and flamboyantly
452
- colored text telling you that your tests passed. None of this output is
453
- needed. Your tests should run fast enough to not require progress reports.
454
- The lack of output provides you with the same amount of information as
455
- reporting success. Test count summaries are only useful if you’re worried that
456
- your tests aren’t being run, but if you worry about that, then providing such
457
- output doesn’t really help. Testing your tests requires something beyond
458
- reporting some arbitrary count that you would have to verify by hand anyway.
459
-
460
- When errors or failures do occur, however, the relevant information is output
461
- in a format that can easily be parsed by an `'errorformat'` for [Vim][] or with
462
- [Compilation Mode][] for [Emacs][]. Diffs are generated for Strings, Arrays,
463
- Hashes, and I/O.
464
-
465
- [Vim]: http://www.vim.org
466
- [Compilation Mode]: http://www.emacswiki.org/emacs/CompilationMode
467
- [Emacs]: http://www.gnu.org/software/emacs/
468
-
469
- ## External Design ##
470
-
471
- Let’s now look at some of the points made in the introduction in greater
472
- detail.
473
-
474
- Lookout only allows you to set one expectation per test. If you’re testing
475
- behavior with a mock, then only one method-invocation expectation can be set.
476
- If you’re testing state, then only one result can be verified. It may seem
477
- like this would cause unnecessary duplication between tests. While this is
478
- certainly a possibility, when you actually begin to try to avoid such
479
- duplication you find that you often do so by improving your interfaces. This
480
- kind of restriction tends to encourage the use of value objects, which are easy
481
- to test, and more focused objects, which require simpler tests, as they have
482
- less behavior to test, per method. By keeping your interfaces focused you’re
483
- also keeping your tests focused.
484
-
485
- Keeping your tests focused improves, in itself, test isolation, but let’s look
486
- at something that hinders it: setup and teardown methods. Most unit testing
487
- frameworks encourage test fragmentation by providing setup and teardown
488
- methods.
489
-
490
- Setup methods create objects and, perhaps, just their behavior for a set of
491
- tests. This means that you have to look in two places to figure out what’s
492
- being done in a test. This may work fine for few methods with simple set-ups,
493
- but makes things complicated when the number of tests increases and the set-up
494
- is complex. Often, each test further adjusts the previously set-up object
495
- before performing any verifications, further complicating the process of
496
- figuring out what state an object has in a given test.
497
-
498
- Teardown methods clean up after tests, perhaps by removing records from a
499
- database or deleting files from the file-system.
500
-
501
- The duplication that setup methods and teardown methods hope to remove is
502
- better avoided by improving your interfaces. This can be done by providing
503
- better set-up methods for your objects and using idioms such as [Resource
504
- Acquisition Is Initialization][] for guaranteed clean-up, test or no test.
505
-
506
- By not using setup and teardown methods we keep everything pertinent to a test
507
- in the test itself, thus improving test isolation. (You also won’t [slow down
508
- your tests][Broken Test::Unit] by keeping unnecessary state.)
509
-
510
- Most unit test frameworks also allow you to create arbitrary test helper
511
- methods. Lookout doesn’t. The same rationale as that that has been
512
- crystallized in the preceding paragraphs applies. If you need helpers you’re
513
- interface isn’t good enough. It really is as simple as that.
514
-
515
- To clarify: there’s nothing inherently wrong with test helper methods, but they
516
- should be general enough that they reside in their own library. The support
517
- for mocks in Lookout is provided through a set of test helper methods that make
518
- it easier to create mocks than it would have been without them.
519
- [Lookout-rack][] is another example of a library providing test helper methods
520
- (well, one of method, actually) that are very useful in testing web
521
- applications that use [Rack][].
522
-
523
- A final point at which some unit test frameworks try to fragment tests further
524
- is documentation. These frameworks provide ways of describing the whats and
525
- hows of what’s being tested, the rationale being that this will provide
526
- documentation of both the test and the code being tested. Describing how a
527
- stack data structure is meant to work is a common example. A stack is,
528
- however, a rather simple data structure, so such a description provides little,
529
- if any, additional information that can’t be extracted from the implementation
530
- and its tests themselves. The implementation and its tests is, in fact, its
531
- own best documentation. Taking the points made in the previous paragraphs into
532
- account, we should already have simple, self-describing, interfaces that have
533
- easily understood tests associated with them. Rationales for the use of a
534
- given data structure or system-design design documentation is better suited in
535
- separate documentation focused at describing exactly those issues.
536
-
537
- [Resource Acquisition Is Initialization]: http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
538
- [Broken Test::Unit]: http://37signals.com/svn/posts/2742-the-road-to-faster-tests
539
- [Lookout-rack]: http://github.com/now/lookout-rack
540
- [Rack]: http://rack.rubyforge.org
541
-
542
- ## Internal Design ##
543
-
544
- The internal design of Lookout has had a couple of goals.
545
-
546
- * As few external dependencies as possible
547
- * As few internal dependencies as possible
548
- * Internal extensibility provides external extensibility
549
- * As fast load times as possible
550
- * As high a ratio of value objects to mutable objects as possible
551
- * Each object must have a simple, obvious name
552
- * Use mix-ins, not inheritance for shared behavior
553
- * As few responsibilities per object as possible
554
- * Optimizing for speed can only be done when you have all the facts
555
-
556
- ### External Dependencies ###
557
-
558
- Lookout used to depend on Mocha for mocks and stubs. While benchmarking I
559
- noticed that a method in Mocha was taking up more than 300 percent of the
560
- runtime. It turned out that Mocha’s method for cleaning up back-traces
561
- generated when a mock failed was doing something incredibly stupid:
562
-
563
- backtrace.reject{ |l| Regexp.new(@lib).match(File.expand_path(l)) }
564
-
565
- Here `@lib` is a `String` containing the path to the lib subdirectory in the
566
- Mocha installation directory. I reported it, provided a patch five days later,
567
- then waited. Nothing happened. 254 days later, according to [Wolfram
568
- Alpha][254 days], half of my patch was, apparently – I say “apparently”, as I
569
- received no notification – applied. By that time I had replaced the whole
570
- mocking-and-stubbing subsystem and dropped the dependency.
571
-
572
- Many Ruby developers claim that Ruby and its gems are too fast-moving for
573
- normal package-managing systems to keep up. This is testament to the fact
574
- that this isn’t the case and that the real problem is instead related to sloppy
575
- practices.
576
-
577
- Please note that I don’t want to single out the Mocha library nor its
578
- developers. I only want to provide an example where relying on external
579
- dependencies can be “considered harmful”.
580
-
581
- [254 days]: http://www.wolframalpha.com/input/?i=days+between+march+17%2C+2010+and+november+26%2C+2010
582
-
583
- ### Internal Dependencies ###
584
-
585
- Lookout has been designed so as to keep each subsystem independent of any
586
- other. The diff subsystem is, for example, completely decoupled from any other
587
- part of the system as a whole and could be moved into its own library at a time
588
- where that would be of interest to anyone. What’s perhaps more interesting is
589
- that the diff subsystem is itself very modular. The data passes through a set
590
- of filters that depends on what kind of diff has been requested, each filter
591
- yielding modified data as it receives it. If you want to read some rather
592
- functional Ruby I can highly recommend looking at the code in the
593
- `lib/lookout/diff` directory.
594
-
595
- This lookout on the design of the library also makes it easy to extend Lookout.
596
- Lookout-rack was, for example, written in about four hours and about 5 of those
597
- 240 minutes were spent on setting up the interface between the two.
598
-
599
- ### Optimizing For Speed ###
600
-
601
- The following paragraph is perhaps a bit personal, but might be interesting
602
- nonetheless.
603
-
604
- I’ve always worried about speed. The original Expectations library used
605
- `extend` a lot to add new behavior to objects. Expectations, for example, used
606
- to hold the result of their execution (what we now term “evaluation”) by being
607
- extended by a module representing success, failure, or error. For the longest
608
- time I used this same method, worrying about the increased performance cost
609
- that creating new objects for results would incur. I finally came to a point
610
- where I felt that the code was so simple and clean that rewriting this part of
611
- the code for a benchmark wouldn’t take more than perhaps ten minutes. Well,
612
- ten minutes later I had my results and they confirmed that creating new objects
613
- wasn’t harming performance. I was very pleased.
614
-
615
- ### Naming ###
616
-
617
- I hate low lines (underscores). I try to avoid them in method names and I
618
- always avoid them in file names. Since the current “best practice” in the Ruby
619
- community is to put `BeginEndStorage` in a file called `begin_end_storage.rb`,
620
- I only name constants using a single noun. This has had the added benefit that
621
- classes seem to have acquired less behavior, as using a single noun doesn’t
622
- allow you to tack on additional behavior without questioning if it’s really
623
- appropriate to do so, given the rather limited range of interpretation for that
624
- noun. It also seems to encourage the creation of value objects, as something
625
- named `Range` feels a lot more like a value than `BeginEndStorage`. (To reach
626
- object-oriented-programming Nirvana you must achieve complete value.)
627
-
628
-
629
- ## Contributors ##
630
-
631
- Contributors to the original expectations codebase are mentioned there. We
632
- hope no one on that list feels left out of this list. Please [let us
633
- know][Lookout issues] if you do.
368
+ Method stubs are another useful thing to have in a unit testing
369
+ framework. Sometimes you need to override a method that does something a
370
+ test shouldn’t do, like access and alter bank accounts. We can override
371
+ stub out a method by using the ‹#stub› method. Let’s assume that we
372
+ have an ‹Account› class that has two methods, ‹#slips› and ‹#total›.
373
+ ‹#Slips› retrieves the bank slips that keep track of your deposits to the
374
+ ‹Account› from a database. ‹#Total› sums the ‹#slips›. In the following
375
+ test we want to make sure that ‹#total› does what it should do without
376
+ accessing the database. We therefore stub out ‹#slips› and make it
377
+ return something that we can easily control.
634
378
 
635
- * Nikolai Weibull
379
+ expect 6 do |m|
380
+ account = Account.new
381
+ stub(account).slips{ [1, 2, 3] }
382
+ account.total
383
+ end
636
384
 
637
- [Lookout issues]: https://github.com/now/lookout/issues
385
+ As with mock methods, if no body is given, the result will be a stub
386
+ object.
638
387
 
388
+ To make it easy to create objects with a set of stubbed methods there’s
389
+ also a convenience method:
639
390
 
640
- ## License ##
391
+ expect 3 do
392
+ s = stub(:a => 1, :b => 2)
393
+ s.a + s.b
394
+ end
395
+
396
+ Please note that this makes it impossible to stub a method on a Hash, but
397
+ you shouldn’t be doing that anyway. In fact, you should never mock or
398
+ stub methods on value objects.
399
+
400
+ § Integration
401
+
402
+ Lookout can be used from Rake¹. Simply include the following code in
403
+ your ‹Rakefile›:
404
+
405
+ require 'lookout/rake/tasks'
406
+
407
+ Lookout::Rake::Tasks::Test.new
408
+
409
+ If the ‹:default› task hasn’t been defined it will be set to depend on the
410
+ ‹:test› task.
411
+
412
+ As an added bonus you can use Lookout’s own gem² tasks:
413
+
414
+ Lookout::Rake::Tasks::Gem.new
415
+
416
+ This provides tasks to ‹build›, ‹check›, ‹install›, and ‹push› your gem.
417
+
418
+ To use Lookout together with Vim³, place ‹contrib/rakelookout.vim› in
419
+ ‹~/.vim/compiler› and add
420
+
421
+ compiler rakelookout
422
+
423
+ to ‹~/.vim/after/ftplugin/ruby.vim›. Executing ‹:make› from inside Vim
424
+ will now run your tests and an errors and failures can be visited with
425
+ ‹:cnext›. Execute ‹:help quickfix› for additional information.
426
+
427
+ Another useful addition to your ‹~/.vim/after/ftplugin/ruby.vim› file may
428
+ be
429
+
430
+ nnoremap <buffer> <silent> <Leader>M <Esc>:call <SID>run_test()<CR>
431
+ let b:undo_ftplugin .= ' | nunmap <buffer> <Leader>M'
432
+
433
+ function! s:run_test()
434
+ let test = expand('%')
435
+ let line = 'LINE=' . line('.')
436
+ if test =~ '^lib/'
437
+ let test = substitute(test, '^lib/', 'test/', '')
438
+ let line = ""
439
+ endif
440
+ execute 'make' 'TEST=' . shellescape(test) line
441
+ endfunction
442
+
443
+ Now, pressing ‹<Leader>M› will either run all tests for a given class, if
444
+ the implementation file is active, or run the test at or just before the
445
+ cursor, if the test file is active. This is useful if you’re currently
446
+ receiving a lot of errors and/or failures and want to focus on those
447
+ associated with a specific class or on a specific test.
448
+
449
+ ¹ Read more about Rake at http://rake.rubyforge.org/
450
+ ² Get information on RubyGems at http://rubygems.org/
451
+ ³ Find out more about Vim at http://www.vim.org/
452
+
453
+ § Interface Design
454
+
455
+ The default output of Lookout can Spartanly be described as Spartan. If no
456
+ errors or failures occur, no output is generated. This is unconventional,
457
+ as unit testing frameworks tend to dump a lot of information on the user,
458
+ concerning things such as progress, test count summaries, and flamboyantly
459
+ colored text telling you that your tests passed. None of this output is
460
+ needed. Your tests should run fast enough to not require progress reports.
461
+ The lack of output provides you with the same amount of information as
462
+ reporting success. Test count summaries are only useful if you’re worried
463
+ that your tests aren’t being run, but if you worry about that, then
464
+ providing such output doesn’t really help. Testing your tests requires
465
+ something beyond reporting some arbitrary count that you would have to
466
+ verify by hand anyway.
467
+
468
+ When errors or failures do occur, however, the relevant information is
469
+ output in a format that can easily be parsed by an ‹'errorformat'› for Vim
470
+ or with {Compilation Mode}¹ for Emacs². Diffs are generated for Strings,
471
+ Arrays, Hashes, and I/O.
472
+
473
+ ¹ Read up on Compilation mode for Emacs at http://www.emacswiki.org/emacs/CompilationMode
474
+ ² Visit The GNU Foundation’s Emacs’ software page at http://www.gnu.org/software/emacs/
475
+
476
+ § External Design
477
+
478
+ Let’s now look at some of the points made in the introduction in greater
479
+ detail.
480
+
481
+ Lookout only allows you to set one expectation per test. If you’re testing
482
+ behavior with a mock, then only one method-invocation expectation can be
483
+ set. If you’re testing state, then only one result can be verified. It
484
+ may seem like this would cause unnecessary duplication between tests.
485
+ While this is certainly a possibility, when you actually begin to try to
486
+ avoid such duplication you find that you often do so by improving your
487
+ interfaces. This kind of restriction tends to encourage the use of value
488
+ objects, which are easy to test, and more focused objects, which require
489
+ simpler tests, as they have less behavior to test, per method. By keeping
490
+ your interfaces focused you’re also keeping your tests focused.
491
+
492
+ Keeping your tests focused improves, in itself, test isolation, but let’s
493
+ look at something that hinders it: setup and teardown methods. Most unit
494
+ testing frameworks encourage test fragmentation by providing setup and
495
+ teardown methods.
496
+
497
+ Setup methods create objects and, perhaps, just their behavior for a set of
498
+ tests. This means that you have to look in two places to figure out what’s
499
+ being done in a test. This may work fine for few methods with simple
500
+ set-ups, but makes things complicated when the number of tests increases
501
+ and the set-up is complex. Often, each test further adjusts the previously
502
+ set-up object before performing any verifications, further complicating the
503
+ process of figuring out what state an object has in a given test.
504
+
505
+ Teardown methods clean up after tests, perhaps by removing records from a
506
+ database or deleting files from the file-system.
507
+
508
+ The duplication that setup methods and teardown methods hope to remove is
509
+ better avoided by improving your interfaces. This can be done by providing
510
+ better set-up methods for your objects and using idioms such as {Resource
511
+ Acquisition Is Initialization}¹ for guaranteed clean-up, test or no test.
512
+
513
+ By not using setup and teardown methods we keep everything pertinent to a
514
+ test in the test itself, thus improving test isolation. (You also won’t
515
+ {slow down your tests}² by keeping unnecessary state.)
516
+
517
+ Most unit test frameworks also allow you to create arbitrary test helper
518
+ methods. Lookout doesn’t. The same rationale as that that has been
519
+ crystallized in the preceding paragraphs applies. If you need helpers
520
+ you’re interface isn’t good enough. It really is as simple as that.
521
+
522
+ To clarify: there’s nothing inherently wrong with test helper methods, but
523
+ they should be general enough that they reside in their own library. The
524
+ support for mocks in Lookout is provided through a set of test helper
525
+ methods that make it easier to create mocks than it would have been without
526
+ them. Lookout-rack³ is another example of a library providing test helper
527
+ methods (well, one of method, actually) that are very useful in testing web
528
+ applications that use Rack⁴.
529
+
530
+ A final point at which some unit test frameworks try to fragment tests
531
+ further is documentation. These frameworks provide ways of describing the
532
+ whats and hows of what’s being tested, the rationale being that this will
533
+ provide documentation of both the test and the code being tested.
534
+ Describing how a stack data structure is meant to work is a common example.
535
+ A stack is, however, a rather simple data structure, so such a description
536
+ provides little, if any, additional information that can’t be extracted
537
+ from the implementation and its tests themselves. The implementation and
538
+ its tests is, in fact, its own best documentation. Taking the points made
539
+ in the previous paragraphs into account, we should already have simple,
540
+ self-describing, interfaces that have easily understood tests associated
541
+ with them. Rationales for the use of a given data structure or
542
+ system-design design documentation is better suited in separate
543
+ documentation focused at describing exactly those issues.
544
+
545
+ ¹ Read the Wikipedia entry for Resource Acquisition Is Initialization at
546
+ http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
547
+ ² Read how 37signals had problems with slow Test::Unit tests at
548
+ http://37signals.com/svn/posts/2742-the-road-to-faster-tests/
549
+ ³ Visit the Lookout-rack GitHub project page at
550
+ http://github.com/now/lookout-rack/
551
+ ⁴ Visit the Rack Rubyforge project page at
552
+ http://rack.rubyforge.org/
553
+
554
+ § Internal Design
555
+
556
+ The internal design of Lookout has had a couple of goals.
557
+
558
+ • As few external dependencies as possible
559
+ • As few internal dependencies as possible
560
+ • Internal extensibility provides external extensibility
561
+ • As fast load times as possible
562
+ • As high a ratio of value objects to mutable objects as possible
563
+ • Each object must have a simple, obvious name
564
+ • Use mix-ins, not inheritance for shared behavior
565
+ • As few responsibilities per object as possible
566
+ • Optimizing for speed can only be done when you have all the facts
567
+
568
+ § External Dependencies
569
+
570
+ Lookout used to depend on Mocha for mocks and stubs. While benchmarking I
571
+ noticed that a method in Mocha was taking up more than 300 percent of the
572
+ runtime. It turned out that Mocha’s method for cleaning up back-traces
573
+ generated when a mock failed was doing something incredibly stupid:
574
+
575
+ backtrace.reject{ |l| Regexp.new(@lib).match(File.expand_path(l)) }
576
+
577
+ Here ‹@lib› is a ‹String› containing the path to the lib subdirectory in
578
+ the Mocha installation directory. I reported it, provided a patch five
579
+ days later, then waited. Nothing happened. {254 days later}¹, according
580
+ to {Wolfram Alpha}², half of my patch was, apparently – I say “apparently”,
581
+ as I received no notification – applied. By that time I had replaced the
582
+ whole mocking-and-stubbing subsystem and dropped the dependency.
583
+
584
+ Many Ruby developers claim that Ruby and its gems are too fast-moving for
585
+ normal package-managing systems to keep up. This is testament to the fact
586
+ that this isn’t the case and that the real problem is instead related to
587
+ sloppy practices.
588
+
589
+ Please note that I don’t want to single out the Mocha library nor its
590
+ developers. I only want to provide an example where relying on external
591
+ dependencies can be “considered harmful”.
592
+
593
+ ¹ See the Wolfram Alpha calculation at http://www.wolframalpha.com/input/?i=days+between+march+17%2C+2010+and+november+26%2C+2010
594
+ ² Check out the Wolfram Alpha computational knowledge engine at http://www.wolframalpha.com/
595
+
596
+ § Internal Dependencies
597
+
598
+ Lookout has been designed so as to keep each subsystem independent of any
599
+ other. The diff subsystem is, for example, completely decoupled from any
600
+ other part of the system as a whole and could be moved into its own library
601
+ at a time where that would be of interest to anyone. What’s perhaps more
602
+ interesting is that the diff subsystem is itself very modular. The data
603
+ passes through a set of filters that depends on what kind of diff has been
604
+ requested, each filter yielding modified data as it receives it. If you
605
+ want to read some rather functional Ruby I can highly recommend looking at
606
+ the code in the ‹lib/lookout/diff› directory.
607
+
608
+ This lookout on the design of the library also makes it easy to extend
609
+ Lookout. Lookout-rack was, for example, written in about four hours and
610
+ about 5 of those 240 minutes were spent on setting up the interface between
611
+ the two.
612
+
613
+ § Optimizing For Speed
614
+
615
+ The following paragraph is perhaps a bit personal, but might be interesting
616
+ nonetheless.
617
+
618
+ I’ve always worried about speed. The original Expectations library used
619
+ ‹extend› a lot to add new behavior to objects. Expectations, for example,
620
+ used to hold the result of their execution (what we now term “evaluation”)
621
+ by being extended by a module representing success, failure, or error. For
622
+ the longest time I used this same method, worrying about the increased
623
+ performance cost that creating new objects for results would incur. I
624
+ finally came to a point where I felt that the code was so simple and clean
625
+ that rewriting this part of the code for a benchmark wouldn’t take more
626
+ than perhaps ten minutes. Well, ten minutes later I had my results and
627
+ they confirmed that creating new objects wasn’t harming performance. I was
628
+ very pleased.
629
+
630
+ § Naming
631
+
632
+ I hate low lines (underscores). I try to avoid them in method names and I
633
+ always avoid them in file names. Since the current “best practice” in the
634
+ Ruby community is to put ‹BeginEndStorage› in a file called
635
+ ‹begin_end_storage.rb›, I only name constants using a single noun. This
636
+ has had the added benefit that classes seem to have acquired less behavior,
637
+ as using a single noun doesn’t allow you to tack on additional behavior
638
+ without questioning if it’s really appropriate to do so, given the rather
639
+ limited range of interpretation for that noun. It also seems to encourage
640
+ the creation of value objects, as something named ‹Range› feels a lot more
641
+ like a value than ‹BeginEndStorage›. (To reach object-oriented-programming
642
+ Nirvana you must achieve complete value.)
643
+
644
+
645
+ § Contributors
646
+
647
+ Contributors to the original expectations codebase are mentioned there. We
648
+ hope no one on that list feels left out of this list. Please
649
+ {let us know}¹ if you do.
650
+
651
+ • Nikolai Weibull
652
+
653
+ ¹ Add an issue to the Lookout issue tracker at https://github.com/now/lookout/issues
641
654
 
642
- You may use, copy, and redistribute this library under the same [terms][] as
643
- Ruby itself.
644
655
 
645
- [terms]: http://www.ruby-lang.org/en/LICENSE.txt
656
+ § License
657
+
658
+ You may use, copy, and redistribute this library under the same terms¹
659
+ as Ruby itself.
660
+
661
+ ¹ Read the Ruby licensing terms at http://www.ruby-lang.org/en/LICENSE.txt