ryanbriones-ZenTest 3.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/History.txt +523 -0
  2. data/Manifest.txt +69 -0
  3. data/README.txt +110 -0
  4. data/Rakefile +68 -0
  5. data/articles/Article.css +721 -0
  6. data/articles/getting_started_with_autotest.html +532 -0
  7. data/articles/how_to_use_zentest.txt +393 -0
  8. data/bin/autotest +55 -0
  9. data/bin/multiruby +40 -0
  10. data/bin/multiruby_setup +68 -0
  11. data/bin/rails_test_audit +80 -0
  12. data/bin/unit_diff +38 -0
  13. data/bin/zentest +28 -0
  14. data/example.txt +42 -0
  15. data/example1.rb +7 -0
  16. data/example2.rb +15 -0
  17. data/example_dot_autotest.rb +45 -0
  18. data/lib/autotest.rb +654 -0
  19. data/lib/autotest/autoupdate.rb +26 -0
  20. data/lib/autotest/camping.rb +37 -0
  21. data/lib/autotest/cctray.rb +57 -0
  22. data/lib/autotest/discover.rb +6 -0
  23. data/lib/autotest/emacs.rb +35 -0
  24. data/lib/autotest/email_notify.rb +66 -0
  25. data/lib/autotest/fixtures.rb +12 -0
  26. data/lib/autotest/growl.rb +28 -0
  27. data/lib/autotest/heckle.rb +14 -0
  28. data/lib/autotest/html_report.rb +31 -0
  29. data/lib/autotest/jabber_notify.rb +111 -0
  30. data/lib/autotest/kdenotify.rb +14 -0
  31. data/lib/autotest/menu.rb +51 -0
  32. data/lib/autotest/migrate.rb +7 -0
  33. data/lib/autotest/notify.rb +34 -0
  34. data/lib/autotest/once.rb +9 -0
  35. data/lib/autotest/pretty.rb +83 -0
  36. data/lib/autotest/rails.rb +81 -0
  37. data/lib/autotest/rcov.rb +22 -0
  38. data/lib/autotest/redgreen.rb +21 -0
  39. data/lib/autotest/restart.rb +11 -0
  40. data/lib/autotest/screen.rb +73 -0
  41. data/lib/autotest/shame.rb +45 -0
  42. data/lib/autotest/snarl.rb +51 -0
  43. data/lib/autotest/timestamp.rb +9 -0
  44. data/lib/functional_test_matrix.rb +92 -0
  45. data/lib/multiruby.rb +401 -0
  46. data/lib/test/rails.rb +295 -0
  47. data/lib/test/rails/controller_test_case.rb +382 -0
  48. data/lib/test/rails/functional_test_case.rb +79 -0
  49. data/lib/test/rails/helper_test_case.rb +64 -0
  50. data/lib/test/rails/ivar_proxy.rb +31 -0
  51. data/lib/test/rails/pp_html_document.rb +74 -0
  52. data/lib/test/rails/rake_tasks.rb +50 -0
  53. data/lib/test/rails/render_tree.rb +93 -0
  54. data/lib/test/rails/test_case.rb +28 -0
  55. data/lib/test/rails/view_test_case.rb +597 -0
  56. data/lib/test/zentest_assertions.rb +134 -0
  57. data/lib/unit_diff.rb +259 -0
  58. data/lib/zentest.rb +566 -0
  59. data/lib/zentest_mapping.rb +99 -0
  60. data/test/test_autotest.rb +449 -0
  61. data/test/test_help.rb +36 -0
  62. data/test/test_rails_autotest.rb +229 -0
  63. data/test/test_rails_controller_test_case.rb +58 -0
  64. data/test/test_rails_helper_test_case.rb +48 -0
  65. data/test/test_rails_view_test_case.rb +275 -0
  66. data/test/test_unit_diff.rb +319 -0
  67. data/test/test_zentest.rb +566 -0
  68. data/test/test_zentest_assertions.rb +128 -0
  69. data/test/test_zentest_mapping.rb +222 -0
  70. metadata +151 -0
@@ -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
+
data/bin/autotest ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/local/bin/ruby -ws
2
+
3
+ $v ||= false
4
+ $h ||= false
5
+ $f ||= false
6
+ $q ||= false
7
+ $help ||= false
8
+
9
+ if $h or $help then
10
+ help = [
11
+ "autotest [options]",
12
+ nil,
13
+ "options:",
14
+ "\t-h",
15
+ "\t-help\t\tYou're looking at it.",
16
+ nil,
17
+ "\t-v\t\tBe verbose.",
18
+ "\t\t\tPrints files that autotest doesn't know how to map to",
19
+ "\t\t\ttests.",
20
+ nil,
21
+ "\t-q\t\tBe more quiet.",
22
+ nil,
23
+ "\t-f\t\tFast start.",
24
+ "\t\t\tDoesn't initially run tests at start.",
25
+ ]
26
+ STDERR.puts help.join("\n")
27
+ exit 1
28
+ end
29
+
30
+ class Dir
31
+ class << self
32
+ alias :old_index :[]
33
+ def [](*args)
34
+ $-w, old_warn = false, $-w
35
+ old_index(*args)
36
+ ensure
37
+ $-w = old_warn
38
+ end
39
+ end
40
+ end
41
+
42
+ require 'autotest'
43
+ style = Autotest.autodiscover
44
+ target = Autotest
45
+ unless style.empty? then
46
+ mod = "autotest/#{style.join("_")}"
47
+ puts "loading #{mod}"
48
+ begin
49
+ require mod
50
+ rescue LoadError
51
+ abort "Autotest style #{mod} doesn't seem to exist. Aborting."
52
+ end
53
+ target = Autotest.const_get(style.map {|s| s.capitalize}.join)
54
+ end
55
+ target.run
data/bin/multiruby ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby -w
2
+
3
+ require 'multiruby'
4
+
5
+ root_dir = Multiruby.root_dir
6
+ versions = Multiruby.build_and_install
7
+ versions = ENV['VERSIONS'].split(/:/) if ENV.has_key? 'VERSIONS'
8
+
9
+ if ENV.has_key? 'EXCLUDED_VERSIONS' then
10
+ excludes = Regexp.union(*ENV['EXCLUDED_VERSIONS'].split(/:/))
11
+ versions = versions.delete_if { |v| v =~ excludes }
12
+ end
13
+
14
+ results = {}
15
+ versions.each do |version|
16
+ ruby = "#{root_dir}/install/#{version}/bin/ruby"
17
+
18
+ ruby.sub!(/bin.ruby/, 'bin/rbx') if version =~ /rubinius/
19
+
20
+ puts
21
+ puts "VERSION = #{version}"
22
+ cmd = [ruby, ARGV].flatten.map { |s| s =~ /\"/ ? "'#{s}'" : s }.join(' ')
23
+ cmd.sub!(/#{ENV['HOME']}/, '~')
24
+ puts "CMD = #{cmd}"
25
+ puts
26
+ system ruby, *ARGV
27
+ puts
28
+ puts "RESULT = #{$?}"
29
+ results[version] = $?
30
+ end
31
+
32
+ passed, failed = results.keys.partition { |v| results[v] == 0 }
33
+
34
+ puts
35
+ puts "TOTAL RESULT = #{failed.size} failures out of #{results.size}"
36
+ puts
37
+ puts "Passed: #{passed.join(", ")}"
38
+ puts "Failed: #{failed.join(", ")}"
39
+
40
+ exit failed.size