lemon 0.8.4 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.config/cucumber.yml +3 -0
- data/.gitignore +8 -0
- data/.reap/digest +678 -0
- data/.reap/test.reap +7 -0
- data/.ruby +42 -45
- data/Assembly +43 -0
- data/HISTORY.rdoc +10 -0
- data/MANIFEST +65 -0
- data/PROFILE +9 -6
- data/Rakefile +14 -0
- data/VERSION +1 -1
- data/lemon.gemspec +152 -0
- data/lib/lemon.yml +42 -45
- data/lib/lemon/cli.rb +4 -2
- data/lib/lemon/controller/test_runner.rb +4 -0
- data/lib/lemon/model/test_case.rb +122 -40
- data/notes/2010-05-05-coverage.rdoc +47 -0
- data/notes/2010-05-06-files_not_classes.rdoc +19 -0
- data/notes/2010-07-11-acid_testing.rdoc +52 -0
- data/notes/2010-08-02-enforcing-the-unit.md +68 -0
- data/notes/2010-08-03-new-api.md +37 -0
- data/site/.rsync-filter +8 -0
- data/site/assets/images/cut-lemon.png +0 -0
- data/site/assets/images/forkme.png +0 -0
- data/site/assets/images/github-logo.png +0 -0
- data/site/assets/images/lemon.jpg +0 -0
- data/site/assets/images/lemon.svg +39 -0
- data/site/assets/images/lemons-are-good.png +0 -0
- data/site/assets/images/opensource.png +0 -0
- data/site/assets/images/ruby-logo.png +0 -0
- data/site/assets/images/skin.jpg +0 -0
- data/site/assets/images/skin1.jpg +0 -0
- data/site/assets/images/tap.png +0 -0
- data/site/assets/images/title.png +0 -0
- data/site/assets/styles/class.css +6 -0
- data/site/assets/styles/reset.css +17 -0
- data/site/assets/styles/site.css +33 -0
- data/site/index.html +217 -0
- data/work/deprecated/command/abstract.rb +29 -0
- data/work/deprecated/command/coverage.rb +115 -0
- data/work/deprecated/command/generate.rb +124 -0
- data/work/deprecated/command/test.rb +112 -0
- data/work/reference/dsl2.rb +136 -0
- data/work/reference/dynamic_constant_lookup.rb +76 -0
- data/work/sandbox/lib/sample.rb +13 -0
- data/work/sandbox/test/sample_case.rb +12 -0
- data/work/trash/example-cover.rb +5 -0
- data/work/trash/example.rb +16 -0
- metadata +134 -101
- data/.yardopts +0 -7
- data/QED.rdoc +0 -1
data/lib/lemon/cli.rb
CHANGED
@@ -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
|
24
|
+
# Before matching test units.
|
25
25
|
attr :before
|
26
26
|
#attr_accessor :prepare
|
27
27
|
|
28
|
-
# After
|
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
|
-
|
143
|
-
|
144
|
-
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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
|
-
|
175
|
-
|
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
|
183
|
-
|
184
|
-
|
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
|
189
|
-
|
190
|
-
|
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
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
270
|
+
# Define a "before all" procedure.
|
271
|
+
def prepare(&procedure)
|
272
|
+
before(&procedure)
|
273
|
+
end
|
274
|
+
alias_method :Prepare, :prepare
|
199
275
|
|
200
|
-
#
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
+
|