lemon 0.8.4 → 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.config/cucumber.yml +3 -0
  2. data/.gitignore +8 -0
  3. data/.reap/digest +678 -0
  4. data/.reap/test.reap +7 -0
  5. data/.ruby +42 -45
  6. data/Assembly +43 -0
  7. data/HISTORY.rdoc +10 -0
  8. data/MANIFEST +65 -0
  9. data/PROFILE +9 -6
  10. data/Rakefile +14 -0
  11. data/VERSION +1 -1
  12. data/lemon.gemspec +152 -0
  13. data/lib/lemon.yml +42 -45
  14. data/lib/lemon/cli.rb +4 -2
  15. data/lib/lemon/controller/test_runner.rb +4 -0
  16. data/lib/lemon/model/test_case.rb +122 -40
  17. data/notes/2010-05-05-coverage.rdoc +47 -0
  18. data/notes/2010-05-06-files_not_classes.rdoc +19 -0
  19. data/notes/2010-07-11-acid_testing.rdoc +52 -0
  20. data/notes/2010-08-02-enforcing-the-unit.md +68 -0
  21. data/notes/2010-08-03-new-api.md +37 -0
  22. data/site/.rsync-filter +8 -0
  23. data/site/assets/images/cut-lemon.png +0 -0
  24. data/site/assets/images/forkme.png +0 -0
  25. data/site/assets/images/github-logo.png +0 -0
  26. data/site/assets/images/lemon.jpg +0 -0
  27. data/site/assets/images/lemon.svg +39 -0
  28. data/site/assets/images/lemons-are-good.png +0 -0
  29. data/site/assets/images/opensource.png +0 -0
  30. data/site/assets/images/ruby-logo.png +0 -0
  31. data/site/assets/images/skin.jpg +0 -0
  32. data/site/assets/images/skin1.jpg +0 -0
  33. data/site/assets/images/tap.png +0 -0
  34. data/site/assets/images/title.png +0 -0
  35. data/site/assets/styles/class.css +6 -0
  36. data/site/assets/styles/reset.css +17 -0
  37. data/site/assets/styles/site.css +33 -0
  38. data/site/index.html +217 -0
  39. data/work/deprecated/command/abstract.rb +29 -0
  40. data/work/deprecated/command/coverage.rb +115 -0
  41. data/work/deprecated/command/generate.rb +124 -0
  42. data/work/deprecated/command/test.rb +112 -0
  43. data/work/reference/dsl2.rb +136 -0
  44. data/work/reference/dynamic_constant_lookup.rb +76 -0
  45. data/work/sandbox/lib/sample.rb +13 -0
  46. data/work/sandbox/test/sample_case.rb +12 -0
  47. data/work/trash/example-cover.rb +5 -0
  48. data/work/trash/example.rb +16 -0
  49. metadata +134 -101
  50. data/.yardopts +0 -7
  51. data/QED.rdoc +0 -1
@@ -61,7 +61,7 @@ module Lemon
61
61
  def test(scripts)
62
62
  require 'lemon/controller/test_runner'
63
63
 
64
- loadpath = options[:loadpath] || []
64
+ loadpath = options[:loadpath] || ['lib'] # + ['lib'] ?
65
65
  requires = options[:requires] || []
66
66
 
67
67
  loadpath.each{ |path| $LOAD_PATH.unshift(path) }
@@ -74,7 +74,9 @@ module Lemon
74
74
  scripts, :format=>options[:format], :namespaces=>options[:namespaces]
75
75
  )
76
76
 
77
- runner.run
77
+ success = runner.run
78
+
79
+ exit -1 unless success
78
80
  end
79
81
 
80
82
  #
@@ -77,6 +77,9 @@ module Lemon
77
77
  end
78
78
 
79
79
  # Run tests.
80
+ #
81
+ # @return [Boolean]
82
+ # Whether tests ran without error or failure.
80
83
  def run
81
84
  #prepare
82
85
  report.start_suite(suite)
@@ -128,6 +131,7 @@ module Lemon
128
131
  report.finish_case(testcase)
129
132
  end
130
133
  report.finish_suite(suite) #(successes, failures, errors, pendings)
134
+ return record[:error].size + record[:fail].size > 0 ? false : true
131
135
  end
132
136
 
133
137
  # Iterate over suite testcases, filtering out unselected testcases
@@ -21,11 +21,11 @@ module Lemon
21
21
  # Ordered list of testunits.
22
22
  attr :units
23
23
 
24
- # Before any test case units are run.
24
+ # Before matching test units.
25
25
  attr :before
26
26
  #attr_accessor :prepare
27
27
 
28
- # After all test case units are run.
28
+ # After matching test units.
29
29
  attr :after
30
30
  #attr_accessor :cleanup
31
31
 
@@ -88,6 +88,12 @@ module Lemon
88
88
  end
89
89
 
90
90
  # Define a unit test for this case.
91
+ #
92
+ # @example
93
+ # unit :puts => "print message with new line to stdout" do
94
+ # puts "Hello"
95
+ # end
96
+ #
91
97
  def unit(*target, &block)
92
98
  target = target.map{ |x| Hash === x ? x.to_a : x }.flatten
93
99
  method, aspect = *target
@@ -138,10 +144,15 @@ module Lemon
138
144
  end
139
145
  alias_method :omit, :Omit
140
146
 
147
+ # Setup is used to set things up for each unit test.
148
+ # The setup procedure is run before each unit.
141
149
  #
142
- def setup(description=nil, &block)
143
- if block
144
- context = TestContext.new(@testcase, description, &block)
150
+ # @param [String] description
151
+ # A brief description of what the setup procedure sets-up.
152
+ #
153
+ def setup(description=nil, &procedure)
154
+ if procedure
155
+ context = TestContext.new(@testcase, description, &procedure)
145
156
  @context = context
146
157
  #@function = false
147
158
  #@testcase.steps << context
@@ -150,60 +161,131 @@ module Lemon
150
161
  alias_method :Setup, :setup
151
162
  alias_method :Concern, :setup
152
163
  alias_method :concern, :setup
164
+
165
+ # @deprecate This alias will probably not stick around.
153
166
  alias_method :Context, :setup
154
167
  alias_method :context, :setup
155
168
 
169
+ =begin
170
+ # TODO: Currently there is no difference between Setup, Instance and Singleton
171
+
156
172
  ## Define a new test instance for this case.
157
- #def instance(description=nil, &block)
158
- # context = TestInstance.new(@testcase, description, &block)
159
- # @context = context
160
- # @function = false
161
- # #@testcase.steps << context
162
- #end
163
- #alias_method :Instance, :instance
173
+ def instance(description=nil, &block)
174
+ if block
175
+ #context = TestInstance.new(@testcase, description, &block)
176
+ context = TestContext.new(@testcase, description, &block)
177
+ else
178
+ context = TestContext.new(@testcase, description) do
179
+ @testcase.target.new # No arguments!!!
180
+ end
181
+ end
182
+ @context = context
183
+ #@function = false
184
+ #@testcase.steps << context
185
+ end
186
+ alias_method :Instance, :instance
164
187
 
165
188
  # Define a new test singleton for this case.
166
- #def Singleton(description=nil, &block)
167
- # context = TestSingleton.new(@testcase, description, &block)
168
- # @context = context
169
- # @function = true
170
- # #@testcase.steps << context
171
- #end
172
- #alias_method :singleton, :Singleton
189
+ def Singleton(description=nil, &block)
190
+ if block
191
+ #context = TestSingleton.new(@testcase, description, &block)
192
+ context = TestContext.new(@testcase, description, &block)
193
+ else
194
+ context = TestContext.new(@testcase, description){ @testcase.target }
195
+ end
196
+ @context = context
197
+ #@function = true
198
+ #@testcase.steps << context
199
+ end
200
+ alias_method :singleton, :Singleton
201
+ =end
173
202
 
174
- def teardown(&block)
175
- @context.teardown = block
203
+ # Teardown procedure is used to clean-up after each unit test.
204
+ def teardown(&procedure)
205
+ @context.teardown = procedure
176
206
  end
177
207
  alias_method :Teardown, :teardown
178
208
 
179
209
  # TODO: Make Before and After more generic to handle before and after
180
- # units, contexts/concerns, etc.
181
-
182
- # Define a before procedure for this case.
183
- def before(*matches, &block)
184
- @testcase.before[matches] = block
210
+ # units, contexts/concerns, etc?
211
+
212
+ # Define a _complex_ before procedure. The #before method allows
213
+ # before procedures to be defined that are triggered by a match
214
+ # against the unit's target method name or _aspect_ description.
215
+ # This allows groups of tests to be defined that share special
216
+ # setup code.
217
+ #
218
+ # @example
219
+ #
220
+ # unit :puts => "standard output (@stdout)" do
221
+ # puts "Hello"
222
+ # end
223
+ #
224
+ # before /@stdout/ do
225
+ # $stdout = StringIO.new
226
+ # end
227
+ #
228
+ # after /@stdout/ do
229
+ # $stdout = STDOUT
230
+ # end
231
+ #
232
+ # @param [Array<Symbol,Regexp>] matches
233
+ # List of match critera that must _all_ be matched
234
+ # to trigger the before procedure.
235
+ #
236
+ def before(*matches, &procedure)
237
+ @testcase.before[matches] = procedure
185
238
  end
186
239
  alias_method :Before, :before
187
240
 
188
- # Define an after procedure for this case.
189
- def after(*matches, &block)
190
- @testcase.after[matches] = block
241
+ # Define a _complex_ after procedure. The #before method allows
242
+ # before procedures to be defined that are triggered by a match
243
+ # against the unit's target method name or _aspect_ description.
244
+ # This allows groups of tests to be defined that share special
245
+ # teardown code.
246
+ #
247
+ # @example
248
+ #
249
+ # unit :puts => "standard output (@stdout)" do
250
+ # puts "Hello"
251
+ # end
252
+ #
253
+ # before /@stdout/ do
254
+ # $stdout = StringIO.new
255
+ # end
256
+ #
257
+ # after /@stdout/ do
258
+ # $stdout = STDOUT
259
+ # end
260
+ #
261
+ # @param [Array<Symbol,Regexp>] matches
262
+ # List of match critera that must _all_ be matched
263
+ # to trigger the after procedure.
264
+ #
265
+ def after(*matches, &procedure)
266
+ @testcase.after[matches] = procedure
191
267
  end
192
268
  alias_method :After, :after
193
269
 
194
- #
195
- #def prepare(&block)
196
- # @testcase.prepare = block
197
- #end
198
- #alias_method :Prepare, :prepare
270
+ # Define a "before all" procedure.
271
+ def prepare(&procedure)
272
+ before(&procedure)
273
+ end
274
+ alias_method :Prepare, :prepare
199
275
 
200
- #
201
- #def cleanup(&block)
202
- # @testcase.cleanup = block
203
- #end
204
- #alias_method :Cleanup, :cleanup
276
+ # Define an "after all" procedure.
277
+ def cleanup(&procedure)
278
+ after(&procedure)
279
+ end
280
+ alias_method :Cleanup, :cleanup
205
281
 
206
- # Load a helper script applicable to this test case.
282
+ # Load a helper script applicable to this test case. Unlike requiring
283
+ # a helper script, the #helper method will eval the file's contents
284
+ # directly into the test context (using instance_eval).
285
+ #
286
+ # @param [String] file
287
+ # File to eval into test context.
288
+ #
207
289
  def helper(file)
208
290
  instance_eval(File.read(file), file)
209
291
  end
@@ -0,0 +1,47 @@
1
+ = 2010-05-05 Getting Good Coverage
2
+
3
+ Getting good coverage analysis is not easy. The difficulty comes
4
+ is determining what is of interest to the tests and what
5
+ is not. That isn't so hard with completely new code, but
6
+ core extensions, for instance, are challenging to isolate
7
+ from original methods.
8
+
9
+ Thee issue can mostly be taken care of by taking a canonical
10
+ snapshot of the system before loading the tests. Then taking
11
+ a snapshot after loading the tests and comparing the two.
12
+ For the most part the difference will be the code in need of
13
+ coverage.
14
+
15
+ Unfortunately helper code still gets in the way of taking these
16
+ snapshots. As a result I have determined there are only four
17
+ possible complete solutions.
18
+
19
+ 1) Use a Ruby lexer/parser to syntatically deconstruct the
20
+ target libraries. This is a complex solution, and not one
21
+ I relish trying.
22
+
23
+ 2) All helpers must go in special files that are preloaded so
24
+ the snapshot can isolate it from the actual target code. This
25
+ means any mocks, for instance, would have to be defined in
26
+ separate files for the the test(s) that might use them. This
27
+ solution it good in that it actually encourges the writing
28
+ reusable helper code, but it can be annoying when writting
29
+ one-off mocks and such.
30
+
31
+ 3) As an alternate to #2, Lemon could provide a method
32
+ for specifying that a class/module or method is a helper
33
+ and can simply be ignored when analysing coverage.
34
+
35
+ 4) Lastly, the library file being covered can be specified
36
+ with a special method, e.g. #Covers. The method would act
37
+ just like +#require+ except that it takes a snapshot before and
38
+ after the loading the library, and in this way builds up
39
+ a table of proper coverage targets.
40
+
41
+ Options #2 and #3 have proven lack luster. It's simply too
42
+ much overhead involved to get good coverage. Option #4 is the most
43
+ robust choice. Unfortunately coverage reports can be very slow
44
+ if there are lot of files to cover --Snapshots can take a second
45
+ or two to create. Hopefully it can be sped up with refinement,
46
+ but at the very least the coverage reporting if optional.
47
+
@@ -0,0 +1,19 @@
1
+ = 2010-05-06 Files, Not Classes
2
+
3
+ Just had a realization about my approch to Lemon tests.
4
+ Until now I have equated the test-case to the class/module
5
+ and the test-unit to the method. That makes sense, but it means
6
+ I have left out a very important factor in the the design
7
+ of tests: the file.
8
+
9
+ I had defined a test-suite as the entire set of tests to
10
+ be run. But I think now I am mistaken. The suite should
11
+ correspond to the fiel being tested. Often that will mean
12
+ one test-case per test-suite, but that's okay.
13
+
14
+ After the next release, 0.7.0, I will implement this
15
+ refactorization. I beleive it should ultimately help
16
+ improve test coverage as well.
17
+
18
+
19
+
@@ -0,0 +1,52 @@
1
+ = 2010-07-11 | Kool-Aid
2
+
3
+ Taking Unit Testing to the next level with a dab of DBC.
4
+
5
+ Say we have,
6
+
7
+ class K
8
+
9
+ def f1(a1)
10
+ g1(a1)
11
+ end
12
+
13
+ def g1(a1)
14
+ a1 + 1
15
+ end
16
+
17
+ end
18
+
19
+ Now what do we want to say about K#f1?
20
+
21
+ unit K, :f1 do |a|
22
+ case a
23
+ when 1
24
+ result do |r|
25
+ r.assert == 2
26
+ end
27
+ end
28
+ end
29
+
30
+ Now later we induce labor.
31
+
32
+ k = K.new
33
+ k.f1(1)
34
+
35
+ It does't really matter what the result class contains, or how it
36
+ even gets called. Thus it can test side-effects as well as simple
37
+ functional results.
38
+
39
+ Okay, so how do we implement this?
40
+
41
+ class K
42
+
43
+ alias "f1:lemon", :f1
44
+
45
+ def f1(a1)
46
+ Lemon.verify_unit(binding) do
47
+ send("f1:lemon", a1)
48
+ end
49
+ end
50
+
51
+ end
52
+
@@ -0,0 +1,68 @@
1
+ 2010-08-02 | Enforcing the Unit
2
+
3
+ The current API for creating a Lemon unit testcase looks like this:
4
+
5
+ covers 'someclass'
6
+
7
+ testcase SomeClass do
8
+
9
+ unit :some_method => "does something or another" do
10
+ # ...
11
+ end
12
+
13
+ end
14
+
15
+ As it currentlt standa the current design is little more than a means of
16
+ organization, orienting the developer to think in terms of test units.
17
+ What is does not do is enforce the actual testing the the unit referenced.
18
+ We could put any old mess in the unit block and as long as it did not raise
19
+ an exception, it would get a *pass*.
20
+
21
+ Taking some time to consider this in depth, I've concieved of a way in which
22
+ that use of the method could in fact be enforced.
23
+
24
+ covers 'someclass'
25
+
26
+ testcase SomeClass do
27
+
28
+ setup do
29
+ SomeClass.new
30
+ end
31
+
32
+ unit :some_method => "does something or another" do |unit|
33
+ unit.object # object from setup
34
+ unit.call(...) # calls #some_method on unit.object
35
+ end
36
+
37
+ end
38
+
39
+ What is intersting about this, beyond that fact that it enforces the use of
40
+ the class or module and method involved, but that it also does so in
41
+ a way naturally suited to mocking --the `unit` delegator could even have
42
+ mocking methods built-in.
43
+
44
+ unit :some_method => "does something or another" do |unit|
45
+ unit.receives.foo(:bar) # object from setup would receive this call
46
+ unit.returns(:baz) # the subsequent #call will return this
47
+ unit.call(...) # calls #some_method on unit.object
48
+ end
49
+
50
+ On the downside this approach limits what can be done in the unit block.
51
+ One _has_ to utilize the object as defined in `setup` and one _has_ to invoke
52
+ the unit method via the `#call` interface. Though, I suppose one could argue
53
+ that these limitations are a good thing, as they help the unit stay narrowly
54
+ focused on that goal at hand.
55
+
56
+ I think this approach is worth considering for a possible furture version.
57
+ Perhaps a "Lemon 2.0". For the time being I believe we can enforce the unit
58
+ without resorting this major API change.
59
+
60
+ The next release of Lemon will temporarily override the unit method on the
61
+ target class for each unit execution. If the unit method gets called within
62
+ the unit block, then it will be noted by the overridden method before passing
63
+ off to the original definition. The approach is perhaps a bit draconian, and
64
+ is certainly only possible thanks to the remarkable dynamicism of Ruby, but
65
+ it should work perfectly well. So now, if the target method is not called within
66
+ the taget block, the unit will raise a Pending exception, regardless of the
67
+ code in the block. Unit Enforced!
68
+
@@ -0,0 +1,37 @@
1
+ # 2010-08-03 | A New API
2
+
3
+ Simplified API. There is one main method.
4
+
5
+ unit_test SomeClass, :some_method, "description" do
6
+ # test ...
7
+ end
8
+
9
+ It will be a global method. The block notations would still work, but
10
+ they would simple become wrappers for the main method.
11
+
12
+ testcase SomeClass do
13
+
14
+ unit :some_method, "description" do
15
+
16
+ end
17
+
18
+ end
19
+
20
+ If I can make it backward compatible, I may also allow something like:
21
+
22
+ testcase SomeClass do
23
+
24
+ unit :some_method do
25
+
26
+ concern "description" do
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ By using this global method, I should be able to simplify the underlying
35
+ implementation a great deal, which has been major concern about Lemon
36
+ as of late.
37
+