ZenTest 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,104 @@
1
+ *** 3.0.0 / 2006-03-06
2
+
3
+ + 2 major enhancements
4
+ + Added autotest and rails_autotest. YAY for continous testing!
5
+ + Repackaged and gemified. YAY for gemification!
6
+ + 3 minor enhancements
7
+ + Added non-mappable tests starting with test_integration_.
8
+ + Lots of code and test refactoring and cleanup.
9
+ + Massive improvement on unit tests.
10
+ + 3 bug fixes
11
+ + Cleaned up class method inheritence. Esp relevant for rails testing.
12
+ + Finally fixed the unit_diff parse bug!
13
+ + Fixed improper counting of errors if a class was missing, should be 1 + missing methods.
14
+
15
+ *** 2.4.0 / 2005-03-21
16
+
17
+ + 3 minor enhancements
18
+ + Able to audit standard class library (so now we can audit rubicon!).
19
+ + Able to map against class methods (self.blah <=> test_class_blah).
20
+ + Added -I=rubypath support
21
+ + 4 bug fixes
22
+ + bug:1151 Fixed stupid problem w/ unit_diff.
23
+ + bug:1454 code generation correctly matches class/module for nested classes.
24
+ + bug:1455 Updated method mapping to work on all operators listed in my quickref.
25
+ + Realized I'm a moron and did NOT release in March like I thought...
26
+
27
+ *** 2.3.0 / 2004-11-18
28
+
29
+ + 6 minor enhancements
30
+ + Massively expanded the method name mappings.
31
+ + Added -r flag to reverse map names, for Rails style testing.
32
+ + Added -e to auto eval tests generated.
33
+ + Added -b & -c flags in unit_diff (passed to diff)
34
+ + Added install and uninstall rules to Makefile.
35
+ + Added some more doco to README.txt
36
+ + 7 bug fixes
37
+ + Cleaned up and refactored tests.
38
+ + Changed the way files are generated, to accomodate new flags.
39
+ + Added some more tests.
40
+ + Added $ZENTEST=true
41
+ + Fixed the one-liner diff bug.
42
+ + Cleaned up multi-line string diffs by unescaping \n
43
+ + Cleaned result for flunks.
44
+
45
+ *** 2.2.0 / 2004-10-18
46
+
47
+ + 4 minor enhancements
48
+ + Added LinuxJournalArticle.txt! WOOT!
49
+ + Added unit_diff.rb - a very cool filter for test output!
50
+ + Extended ZenTest to work with standard input
51
+ + Added "ZenTest FULL" to force ZenTest to analyze inherited methods, for subclasses of the standard library.
52
+ + 3 bug fixes
53
+ + Extended makefile to be more dynamic and stop diffing versions. ugh.
54
+ + Expanded the method rename map to handle <<, *, +, and ==.
55
+ + Added more test cases.
56
+
57
+ *** 2.1.2 / 2004-03-08
58
+
59
+ + 3 bug fixes
60
+ + Fixed yet another 1.8ism, results will be the same in 1.8 and 1.6.
61
+ + Fixed code responsible for method name conversion.
62
+ + I am a moron... didn't run tests after updating version.
63
+ + Need to convert my diff-based tests to unit tests.
64
+
65
+ *** 2.1.1 / 2004-03-06
66
+
67
+ + 3 bug fixes
68
+ + Fixed a 1.8ism.
69
+ + Removed zentestrunner. Older ruby users will just have to suffer.
70
+ + Updated history to ZenWeb format.
71
+
72
+ *** 2.1.0 / 2003-01-07
73
+
74
+ + 3 major enhancements
75
+ + Output is runnable as-is thanks to zentestrunner.rb.
76
+ + Wrapped up all running functionality into ZenTest.fix
77
+ + Added simple statistic output... Thanks Dave & Andy!!!
78
+ + 1 minor enhancement
79
+ + Added zentestrunner.rb until Nathaniel accepts my changes!
80
+ + 3 bug fixes
81
+ + Removed at_exit override and avoided test/unit altogether.
82
+ + Added a clean rule to Makefile
83
+ + Extended README.txt to include some of the rules.
84
+ + Fixed several tests and added assertions for new stats
85
+
86
+ *** 2.0.0 / 2002-10-29
87
+
88
+ + 2 major enhancements
89
+ + Rewrite of ZenTest.rb into actual OO design.
90
+ + Added unit tests, finally...
91
+
92
+ *** 1.0.1 / 2002-09-28
93
+
94
+ + 1 minor enhancements
95
+ + Only loads when a class is detected, allows some scripts to be skipped.
96
+ + 3 bug fixes
97
+ + Cleaned up output. Verbose when $DEBUG is true.
98
+ + Added an error count that is output at end.
99
+ + Better filtering or conversion on some method names.
100
+
101
+ *** 1.0.0 / 2002-09-24
102
+
103
+ + 1 major enhancements
104
+ + Birthday!
@@ -0,0 +1,393 @@
1
+ How to Use ZenTest with Ruby
2
+ by Pat Eyler <pate@kohalabs.com>
3
+ http://linuxjournal.com/article.php?sid=7776
4
+ (included in this package with permission)
5
+
6
+ Refactoring and unit testing are a great pair of tools for every
7
+ programmer's workbench. Sadly, not every programmer knows how to use
8
+ them. My first exposure to them came when I started using Ruby,
9
+ refactoring and unit testing are a big part of the landscape in the
10
+ Ruby community.
11
+
12
+ Some time ago, I translated the refactoring example from the first
13
+ chapter of Martin Fowler's excellent book, Refactoring, out of Java
14
+ and into Ruby. I felt this would be a great way to learn more about
15
+ refactoring and brush up on my Ruby while I was at it. Recently, I
16
+ decided to update the translation for Ruby 1.8.X. One of the things I
17
+ needed to change was to convert the old unit tests to work with
18
+ Test::Unit, the new unit testing framework for Ruby.
19
+
20
+ I wasn't really looking forward to building a new test suite though.
21
+ Fortunately, help was available. Ryan Davis has written a great tool
22
+ called ZenTest, which creates test suites for existing bodies of
23
+ code. Since a lot of people are new to refactoring, unit testing, and
24
+ ZenTest, I thought this would be a great chance to introduce you to
25
+ this trio of tools.
26
+
27
+ Martin's example code is built around a video store application. In
28
+ his original code, there are three classes; Customer, Movie, and
29
+ Rental. I'll focus on just the Customer class in this article.
30
+ Here's the original code:
31
+
32
+ class Customer
33
+ attr_accessor :name
34
+
35
+ def initialize(name)
36
+ @name = name
37
+ @rentals = Array.new
38
+ end
39
+
40
+ def addRental(aRental)
41
+ @rentals.push(aRental)
42
+ end
43
+
44
+ def statement
45
+ totalAmount = 0.0
46
+ frequentRenterPoints = 0
47
+ rentals = @rentals.length
48
+ result = "\nRental Record for #{@name}\n"
49
+ thisAmount = 0.0
50
+ @rentals.each do |rental|
51
+ # determine amounts for each line
52
+ case rental.aMovie.pricecode
53
+ when Movie::REGULAR
54
+ thisAmount += 2
55
+ if rental.daysRented > 2
56
+ thisAmount += (rental.daysRented - 2) * 1.5
57
+ end
58
+
59
+ when Movie::NEW_RELEASE
60
+ thisAmount += rental.daysRented * 3
61
+
62
+ when Movie::CHILDRENS
63
+ thisAmount += 1.5
64
+ if each.daysRented > 3
65
+ thisAmount += (rental.daysRented - 3) * 1.5
66
+ end
67
+
68
+ end
69
+
70
+ # add frequent renter points
71
+ frequentRenterPoints += 1
72
+ # add bonus for a two day new release rental
73
+ if ( rental.daysRented > 1) &&
74
+ (Movie::NEW_RELEASE == rental.aMovie.pricecode)
75
+ frequentRenterPoints += 1
76
+ end
77
+
78
+ # show figures for this rental
79
+ result +="\t#{rental.aMovie.title}\t#{thisAmount}\n"
80
+ totalAmount += thisAmount
81
+ end
82
+ result += "Amount owed is #{totalAmount}\n"
83
+ result += "You earned #{frequentRenterPoints} frequent renter points"
84
+ end
85
+ end
86
+
87
+
88
+ Not the cleanest code in the world, but it is supposed to be that
89
+ way. This represents the code as you get it from the user. No
90
+ tests, poorly laid out, but working -- and it's your job to make it
91
+ better without breaking it. So, where to start? With unit tests of
92
+ course.
93
+
94
+ Time to grab ZenTest. You can run it like this:
95
+
96
+ $ zentest videostore.rb > test_videostore.rb
97
+
98
+ which produces a file full of tests. Running the test suite doesn't
99
+ do quite what we were hoping though:
100
+
101
+ $ ruby testVideoStore.rb Loaded suite testVideoStore
102
+ Started
103
+ EEEEEEEEEEE
104
+ Finished in 0.008974 seconds.
105
+
106
+ 1) Error!!!
107
+ test_addRental(TestCustomer):
108
+ NotImplementedError: Need to write test_addRental
109
+ testVideoStore.rb:11:in `test_addRental'
110
+ testVideoStore.rb:54
111
+
112
+ 2) Error!!!
113
+ test_name=(TestCustomer):
114
+ NotImplementedError: Need to write test_name=
115
+ testVideoStore.rb:15:in `test_name='
116
+ testVideoStore.rb:54
117
+
118
+ 3) Error!!!
119
+ test_statement=(TestCustomer):
120
+ NotImplementedError: Need to write test_statement
121
+ testVideoStore.rb:19:in `test_statement'
122
+ testVideoStore.rb:54
123
+ .
124
+ .
125
+ .
126
+
127
+ 11 tests, 0 assertions, 0 failures, 11 errors
128
+ $
129
+
130
+ So what exactly did we get out of this? Here's the portion of our
131
+ new test suite that matters for the Customer class:
132
+
133
+ # Code Generated by ZenTest v. 2.1.2
134
+ # classname: asrt / meth = ratio%
135
+ # Customer: 0 / 3 = 0.00%
136
+
137
+ require 'test/unit'
138
+
139
+ class TestCustomer < Test::Unit::TestCase
140
+ def test_addRental
141
+ raise NotImplementedError, 'Need to write test_addRental'
142
+ end
143
+
144
+ def test_name=
145
+ raise NotImplementedError, 'Need to write test_name='
146
+ end
147
+
148
+ def test_statement
149
+ raise NotImplementedError, 'Need to write test_statement'
150
+ end
151
+ end
152
+
153
+ ZenTest built three test methods: one for the accessor method, one for
154
+ the addRental method, and one for the statement method. Why nothing
155
+ for the initializer? Well, initializers tend to be pretty bulletproof
156
+ (if they're not, it's pretty easy to add the test method yourself).
157
+ Besides, we'll be testing it indirectly when we write test_name= (the
158
+ tests for the accessor method). There's one other thing we'll need to
159
+ add, the test suite doesn't load the code we're testing. Changing the
160
+ beginning of the script to require the videostore.rb file will do the
161
+ trick for us.
162
+
163
+
164
+ # Code Generated by ZenTest v. 2.1.2
165
+ # classname: asrt / meth = ratio%
166
+ # Customer: 0 / 3 = 0.00%
167
+
168
+ require 'test/unit'
169
+ require 'videostore'
170
+
171
+ That little snippet of comments at the top lets us know that we have three
172
+ methods under test in the Customer class, zero assertions testing
173
+ them, and no coverage. Let's fix that. We'll start by writing some
174
+ tests for test_name= (no, it really doesn't matter what order we go in --
175
+ this is just a convenient place to start).
176
+
177
+ def test_name=
178
+ aCustomer = Customer.new("Fred Jones")
179
+ assert_equal("Fred Jones",aCustomer.name)
180
+ aCustomer.name = "Freddy Jones"
181
+ assert_equal("Freddy Jones",aCustomer.name
182
+ end
183
+
184
+ Running testVideoStore.rb again gives us:
185
+
186
+ $ ruby testVideoStore.rb
187
+ Loaded suite testVideoStore
188
+ Started
189
+ E.EEEEEEEEE
190
+ Finished in 0.011233 seconds.
191
+
192
+ 1) Error!!!
193
+ test_addRental(TestCustomer):
194
+ NotImplementedError: Need to write test_addRental
195
+ testVideoStore.rb:13:in `test_addRental'
196
+ testVideoStore.rb:58
197
+
198
+ 2) Error!!!
199
+ test_statement(TestCustomer):
200
+ NotImplementedError: Need to write test_statement
201
+ testVideoStore.rb:23:in `test_statement'
202
+ testVideoStore.rb:58
203
+ .
204
+ .
205
+ .
206
+ 11 tests, 2 assertions, 0 failures, 10 errors
207
+ $
208
+
209
+ So far, so good. The line of 'E's (which shows errors in the test run)
210
+ has been reduced by one, and the summary line at the bottom tells us
211
+ roughly the same thing.
212
+
213
+ We really don't have a way to test addRental directly, so we'll just
214
+ write an stub test for now.
215
+
216
+ def test_addRental
217
+ assert(1) # stub test, since there is nothing in the method to test
218
+ end
219
+
220
+ When we run the tests again, we get:
221
+
222
+ $ ruby testVideoStore.rb
223
+ Loaded suite testVideoStore
224
+ Started
225
+ ..EEEEEEEEE
226
+ Finished in 0.008682 seconds.
227
+
228
+ 1) Error!!!
229
+ test_statement(TestCustomer):
230
+ NotImplementedError: Need to write test_statement
231
+ testVideoStore.rb:22:in `test_statement'
232
+ testVideoStore.rb:57
233
+ .
234
+ .
235
+ .
236
+ 11 tests, 3 assertions, 0 failures, 9 errors
237
+ $
238
+
239
+ Better and better, just one error left in the TestCustomer class.
240
+ Let's finish up with a test that will clear our test_statement error
241
+ and verify that addRental works correctly:
242
+
243
+ def test_statement
244
+ aMovie = Movie.new("Legacy",0)
245
+
246
+ aRental = Rental.new(aMovie,2)
247
+
248
+ aCustomer = Customer.new("Fred Jones")
249
+ aCustomer.addRental(aRental)
250
+ aStatement = "\nRental Record for Fred Jones\n\tLegacy\t2.0
251
+ Amount owed is 2.0\nYou earned 1 frequent renter points"
252
+
253
+ assert_equal(aStatement,aCustomer.statement)
254
+
255
+ end
256
+
257
+ We run the tests again, and see:
258
+
259
+ $ ruby testVideoStore.rb
260
+ Loaded suite testVideoStore
261
+ Started
262
+ ...EEEEEEEE
263
+ Finished in 0.009378 seconds.
264
+ .
265
+ .
266
+ .
267
+ 11 tests, 4 assertions, 0 failures, 8 errors
268
+ $
269
+
270
+ Great! The only errors left are on the Movie and Rental classes,
271
+ the Customer class is clean.
272
+
273
+ We can continue along like this for the remaining classes, but I'll
274
+ not bore you with those details. Instead, I'd like to look at how
275
+ ZenTest can help when you've already got some tests in place. Later
276
+ development allows us to do just that -- the video store owner
277
+ wants a new web based statement for web using customers.
278
+
279
+ After a bit of refactoring and new development, the code looks like
280
+ this:
281
+
282
+ class Customer
283
+ attr_accessor :name
284
+
285
+ def initialize(name)
286
+ @name = name
287
+ @rentals = Array.new
288
+ end
289
+
290
+ def addRental(aRental)
291
+ @rentals.push(aRental)
292
+ end
293
+
294
+ def statement
295
+ result = "\nRental Record for #{@name}\n"
296
+ @rentals.each do
297
+ |each|
298
+ # show figures for this rental
299
+ result +="\t#{each.aMovie.title}\t#{each.getCharge}\n"
300
+ end
301
+ result += "Amount owed is #{getTotalCharge}\n"
302
+ result +=
303
+ "You earned #{getFrequentRenterPoints} frequent renter points"
304
+ end
305
+
306
+ def htmlStatement
307
+ result = "\n<H1>Rentals for <EM>#{name}</EM></H1><P>\n"
308
+ @rentals.each do
309
+ |each|
310
+ result += "#{each.aMovie.title}: #{each.getCharge}<BR>\n"
311
+ end
312
+ result += "You owe <EM>#{getTotalCharge}</EM><P>\n"
313
+ result +=
314
+ "On this rental you earned <EM>#{getFrequentRenterPoints}" +
315
+ "</EM> frequent renter points<P>"
316
+ end
317
+
318
+ def getTotalCharge
319
+ result = 0.0
320
+ @rentals.each do
321
+ |each|
322
+ result += each.getCharge()
323
+ end
324
+ result
325
+ end
326
+
327
+ def getFrequentRenterPoints
328
+ result = 0
329
+ @rentals.each do
330
+ |each|
331
+ result += each.getFrequentRenterPoints
332
+ end
333
+ result
334
+ end
335
+ end
336
+
337
+ There's a lot of new stuff in here. If we run ZenTest again, it'll
338
+ pick up the methods we don't have any coverage on (we should have
339
+ written them as we wrote the new methods, but this is a bit more
340
+ illustrative). This time, we'll invoke ZenTest a little bit
341
+ differently:
342
+
343
+ $ zentest videostore.rb testVideoStore.rb > Missing_tests
344
+
345
+ and our (trimmed) output looks like this:
346
+
347
+ # Code Generated by ZenTest v. 2.1.2
348
+ # classname: asrt / meth = ratio%
349
+ # Customer: 4 / 6 = 66.67%
350
+
351
+
352
+ require 'test/unit'
353
+
354
+ class TestCustomer < Test::Unit::TestCase
355
+ def test_getFrequentRenterPoints
356
+ raise NotImplementedError,
357
+ 'Need to write test_getFrequentRenterPoints'
358
+ end
359
+
360
+ def test_getTotalCharge
361
+ raise NotImplementedError, 'Need to write test_getTotalCharge'
362
+ end
363
+
364
+ def test_htmlStatement
365
+ raise NotImplementedError, 'Need to write test_htmlStatement'
366
+ end
367
+ end
368
+
369
+ Hmmm, three more test methods to fill in to get our complete
370
+ coverage. As we write these, we can just migrate them into our
371
+ existing testVideoStore.rb test suite. Then we can keep moving ahead
372
+ with refactoring and adding new features. In the future, let's just
373
+ be sure we add tests as we go along. ZenTest can help you here too.
374
+ You can write stubs for new development, then run ZenTest to create
375
+ your new test stubs as well. After some refactorings (like 'extract
376
+ method'), ZenTest can be used the same way.
377
+
378
+ Refactoring and unit testing are powerful tools for programmers, and
379
+ ZenTest provides an easy way to start using them in a Ruby
380
+ environment. Hopefully, this introduction has whetted your appetite.
381
+
382
+ If you're interested in learning more about refactoring, please grab a
383
+ copy of 'Refactoring: Improving the Design of Existing Code' and take
384
+ a look at www.refactoring.com. For more information about unit
385
+ testing, please see: c2.com/cgi/wiki?UnitTest,
386
+ www.junit.org/index.htm, and
387
+ www.extremeprogramming.org/rules/unittests.html.
388
+
389
+ The latest information about Test::Unit and ZenTest are available at
390
+ their home pages: testunit.talbott.ws (for Test::Unit) and
391
+ www.zenspider.com/ZSS/Products/ZenTest.
392
+
393
+