ae 1.0.0 → 1.1.0
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/MANIFEST +3 -1
- data/{README → README.rdoc} +40 -17
- data/demo/01_overview.rdoc +25 -30
- data/demo/03_assert.rdoc +55 -22
- data/demo/04_subjunctive.rdoc +32 -59
- data/demo/05_expect.qed +83 -0
- data/doc/qedoc/index.html +119 -113
- data/lib/ae.rb +1 -0
- data/lib/ae/assertor.rb +14 -0
- data/lib/ae/core_ext.rb +2 -2
- data/lib/ae/expect.rb +124 -0
- data/meta/homepage +1 -1
- data/meta/version +1 -1
- metadata +8 -8
data/MANIFEST
CHANGED
@@ -4,6 +4,7 @@ demo/01_overview.rdoc
|
|
4
4
|
demo/02_assertion.rdoc
|
5
5
|
demo/03_assert.rdoc
|
6
6
|
demo/04_subjunctive.rdoc
|
7
|
+
demo/05_expect.qed
|
7
8
|
doc/qedoc
|
8
9
|
doc/qedoc/index.html
|
9
10
|
doc/qedoc/jquery.js
|
@@ -13,6 +14,7 @@ lib/ae/assert.rb
|
|
13
14
|
lib/ae/assertion.rb
|
14
15
|
lib/ae/assertor.rb
|
15
16
|
lib/ae/core_ext.rb
|
17
|
+
lib/ae/expect.rb
|
16
18
|
lib/ae/subjunctive
|
17
19
|
lib/ae/subjunctive/must.rb
|
18
20
|
lib/ae/subjunctive/should.rb
|
@@ -35,5 +37,5 @@ meta/title
|
|
35
37
|
meta/version
|
36
38
|
test
|
37
39
|
LICENSE
|
38
|
-
README
|
40
|
+
README.rdoc
|
39
41
|
HISTORY
|
data/{README → README.rdoc}
RENAMED
@@ -1,11 +1,11 @@
|
|
1
1
|
= A.E. -- Assertive Expressive
|
2
2
|
|
3
|
-
* http://
|
3
|
+
* http://proutils.rubyforge.org/ae/
|
4
4
|
|
5
5
|
|
6
6
|
== DESCRIPTION
|
7
7
|
|
8
|
-
|
8
|
+
Assertive Expressive (A.E.) is an assertions framework
|
9
9
|
intended for reuse by any TDD, BDD or similar system.
|
10
10
|
|
11
11
|
|
@@ -14,7 +14,7 @@ intended for reuse by any TDD, BDD or similar system.
|
|
14
14
|
* Clear, simple and concise syntax.
|
15
15
|
* Uses higher-order functions and fluid notation.
|
16
16
|
* Reusable core extensions ease assertion construction.
|
17
|
-
* Core extensions are
|
17
|
+
* Core extensions are standardized around Ruby Facets.
|
18
18
|
* Facets is an optional dependency; extensions are built-in.
|
19
19
|
* Easily extensible allowing for alternate notations.
|
20
20
|
* Eats it's own dog food.
|
@@ -38,12 +38,11 @@ the result of which is likewise verified.
|
|
38
38
|
|
39
39
|
assert{true}
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
like:
|
41
|
+
However, the true power the AE's +assert+ method, lies in it's use
|
42
|
+
without argument or block. In this case, it returns an instance of
|
43
|
+
+Assertor+. An +Assertor+ is an <i>Assertions Functor</i> --also
|
44
|
+
called a Higher-Order Function, it is a function that operates on
|
45
|
+
another function. With it, we can make assertions like:
|
47
46
|
|
48
47
|
x.assert == y
|
49
48
|
|
@@ -53,22 +52,46 @@ like:
|
|
53
52
|
...
|
54
53
|
end
|
55
54
|
|
56
|
-
And so forth.
|
57
|
-
|
55
|
+
And so forth. Any method can be used in conjunction with +assert+
|
56
|
+
to make an assertion. Eg.
|
57
|
+
|
58
|
+
class String
|
59
|
+
def daffy?
|
60
|
+
/daffy/i =~ self
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
"Daffy Duck".assert.daffy?
|
65
|
+
|
66
|
+
Please have a look at the QEDocs and RDocs to learn more.
|
58
67
|
|
59
68
|
|
60
69
|
== HOW TO INSTALL
|
61
70
|
|
62
|
-
|
71
|
+
=== Gem Installs
|
72
|
+
|
73
|
+
AE releases it's gems via Gemcutter. If you don't have Gemcutter
|
74
|
+
installed do:
|
75
|
+
|
76
|
+
$ gem install gemcutter
|
77
|
+
$ gem tumble
|
78
|
+
|
79
|
+
Then you can install AE with:
|
63
80
|
|
64
81
|
gem install ae
|
65
82
|
|
66
|
-
|
67
|
-
|
83
|
+
=== Site Installs
|
84
|
+
|
85
|
+
Local installation requires Setup.rb.
|
86
|
+
|
87
|
+
gem install setup
|
88
|
+
|
89
|
+
Then download the tarball package from GitHub (under pack/ directory)
|
90
|
+
and do:
|
68
91
|
|
69
|
-
tar -xvzf ae-1.0.0.tgz
|
70
|
-
cd ae-1.0.0.tgz
|
71
|
-
sudo setup.rb all
|
92
|
+
$ tar -xvzf ae-1.0.0.tgz
|
93
|
+
$ cd ae-1.0.0.tgz
|
94
|
+
$ sudo setup.rb all
|
72
95
|
|
73
96
|
Windows users use 'ruby setup.rb all'.
|
74
97
|
|
data/demo/01_overview.rdoc
CHANGED
@@ -14,19 +14,16 @@ Requiring the AE library.
|
|
14
14
|
require 'ae'
|
15
15
|
|
16
16
|
Loads two classes, +Assertion+ and +Assertor+, the Kernel
|
17
|
-
method +assert+ and it's ancillaries
|
17
|
+
method +assert+ and it's ancillaries <tt>assert!</tt> and +refute+
|
18
18
|
and a set of core extensions that make writing certain types
|
19
19
|
of assertions easier.
|
20
20
|
|
21
21
|
|
22
22
|
== Assertion and Assertor Classes
|
23
23
|
|
24
|
-
The +Assertion+ class is a subclass of +Exception+. It is the
|
25
|
-
error raised when an assertion fails.
|
26
|
-
|
27
24
|
The +Assertion+ class is at the heart of AE. All other AE
|
28
|
-
|
29
|
-
of Exception. When an assertion is made
|
25
|
+
methods depend on it. The +Assertion+ class is a subclass
|
26
|
+
of Exception. When an assertion is made and fails, it is
|
30
27
|
an instance of Assertion that is raised.
|
31
28
|
|
32
29
|
Assertion.assert.raised? do
|
@@ -35,33 +32,31 @@ an instance of Assertion that is raised.
|
|
35
32
|
end
|
36
33
|
|
37
34
|
Like any raised exception, the last Assertion message is available
|
38
|
-
via
|
35
|
+
via <tt>$!</tt>.
|
39
36
|
|
40
|
-
(FYI, in Test::Unit the equivalent class was called
|
41
|
-
AE has adopted the shortened term for my fingers sake ;) Also, recently
|
42
|
-
it was discoverd to be the choosen term in minitest --proving good ideas
|
43
|
-
find their way to the top.)
|
37
|
+
(FYI, in Test::Unit the equivalent class was called +AssertionFailedError+.)
|
44
38
|
|
45
|
-
Assertions
|
46
|
-
behavior specifications. Rather they are used to create
|
39
|
+
Assertions themselves are not generally used in creating tests or
|
40
|
+
behavior specifications. Rather they are used to create additional
|
47
41
|
types of assertion methods.
|
48
42
|
|
49
|
-
As mentioned above the +Assertor+ class is a type of
|
50
|
-
or
|
43
|
+
As mentioned above the +Assertor+ class is a type of Higher-Order
|
44
|
+
function, or Functor, which intercedes with a normal message
|
51
45
|
invocation to monitor for failed conditions, upon which is raises
|
52
46
|
Assertion exceptions.
|
53
47
|
|
54
48
|
|
55
49
|
== Assertion Methods
|
56
50
|
|
57
|
-
The three methods, +assert+,
|
58
|
-
an Assertor instance when used fluidly, i.e. magic-dot
|
59
|
-
higher-order notation, functor notation, whatever you
|
60
|
-
to call it.
|
51
|
+
The three methods, +assert+, <tt>assert!</tt> and +refute+ all
|
52
|
+
return an Assertor instance when used fluidly, i.e. magic-dot
|
53
|
+
notation, higher-order notation, functor notation, whatever you
|
54
|
+
prefer to call it.
|
61
55
|
|
62
56
|
assert(Assertor === assert)
|
63
57
|
|
64
|
-
|
58
|
+
Through the use of +method_missing+, the Assertor allows us to write
|
59
|
+
statements like:
|
65
60
|
|
66
61
|
1.assert == 1
|
67
62
|
|
@@ -72,20 +67,20 @@ is raised.
|
|
72
67
|
1.assert == 2
|
73
68
|
end
|
74
69
|
|
75
|
-
The methods
|
76
|
-
they purport the negative condition. Patterned after Ruby's
|
77
|
-
use of
|
78
|
-
While +refute+ exists for the sake of those
|
79
|
-
a
|
70
|
+
The methods <tt>assert!</tt> and +refute+ are just like +assert+
|
71
|
+
expect they purport the negative condition. Patterned after Ruby's
|
72
|
+
own use of "<tt>!</tt>" as meaning +not+, <tt>assert!</tt> should be
|
73
|
+
read "assert not". While +refute+ exists for the sake of those who
|
74
|
+
find the use of a bang method for this purpose unsuited to them.
|
80
75
|
|
81
76
|
|
82
|
-
== How It
|
77
|
+
== How It Works
|
83
78
|
|
84
79
|
An Assertor essentially sits in wait for a method call (via
|
85
|
-
method_missing). When that happens it applies the method to
|
86
|
-
|
87
|
-
|
88
|
-
|
80
|
+
method_missing). When that happens it applies the method to the
|
81
|
+
original receiver, but wrapped in a clause that raises an
|
82
|
+
Assertion should the statement fail. If we wanted to be
|
83
|
+
pedantic, we could write our assertions like:
|
89
84
|
|
90
85
|
raise Assertion.new("1 != 1") unless 1 == 1
|
91
86
|
|
data/demo/03_assert.rdoc
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
== Compatible with Test::Unit
|
4
4
|
|
5
|
-
The
|
6
|
-
with the same method in
|
5
|
+
The +assert+ method is designed to be backward compatible
|
6
|
+
with the same method in <tt>Test::Unit</tt>.
|
7
7
|
|
8
|
-
Using an argument,
|
8
|
+
Using an argument, +assert+ will check that an argument evaluates
|
9
9
|
to true. Optionally one can send along a meaningful message should
|
10
10
|
the assertion fail.
|
11
11
|
|
@@ -18,7 +18,7 @@ the assertion fail.
|
|
18
18
|
|
19
19
|
== Assert with a Block
|
20
20
|
|
21
|
-
In addition
|
21
|
+
In addition +assert+ has been extended to accept a block. Like the case of the
|
22
22
|
argument, the block is expected to return something that evaluates as true.
|
23
23
|
|
24
24
|
assert do
|
@@ -42,18 +42,18 @@ into the block as a block argument.
|
|
42
42
|
|
43
43
|
== Antonyms for Assert
|
44
44
|
|
45
|
-
We can state the opposite assertion using
|
45
|
+
We can state the opposite assertion using <tt>assert!</tt>.
|
46
46
|
|
47
47
|
10.assert! == 9
|
48
48
|
|
49
|
-
Or, because some people do not like the use of a bang method,
|
49
|
+
Or, because some people do not like the use of a bang method, +refute+.
|
50
50
|
|
51
51
|
10.refute == 9
|
52
52
|
|
53
|
-
These terms can be used just as
|
53
|
+
These terms can be used just as +assert+ is used in all examples,
|
54
54
|
but with the opposite inference.
|
55
55
|
|
56
|
-
Another way to get the opposite inference, is to use
|
56
|
+
Another way to get the opposite inference, is to use +not+.
|
57
57
|
|
58
58
|
10.assert.not == 9
|
59
59
|
|
@@ -65,11 +65,11 @@ Rather then the general form:
|
|
65
65
|
x = 10
|
66
66
|
x.assert.object_id == x.object_id
|
67
67
|
|
68
|
-
We can use Ruby's own
|
68
|
+
We can use Ruby's own <tt>equal?</tt> method.
|
69
69
|
|
70
70
|
x.assert.equal?(x)
|
71
71
|
|
72
|
-
AE provides
|
72
|
+
AE provides <tt>identical?</tt> method as an alternative
|
73
73
|
to make it a bit more clear.
|
74
74
|
|
75
75
|
x.assert.identical?(x)
|
@@ -77,10 +77,10 @@ to make it a bit more clear.
|
|
77
77
|
|
78
78
|
== Equality Assertions
|
79
79
|
|
80
|
-
The most common assertion is that of value equality (
|
80
|
+
The most common assertion is that of value equality (<tt>==</tt>),
|
81
81
|
as we have seen throughout this document. But other forms of
|
82
82
|
equality can be verified as easily. We have already mentioned
|
83
|
-
identity. In addition there is
|
83
|
+
identity. In addition there is <i>type equality</i>.
|
84
84
|
|
85
85
|
17.assert.eql? 17
|
86
86
|
|
@@ -88,7 +88,7 @@ identity. In addition there is *Type Equality*.
|
|
88
88
|
17.assert.eql? 17.0
|
89
89
|
end
|
90
90
|
|
91
|
-
And there is
|
91
|
+
And there is <i>case equality</i>.
|
92
92
|
|
93
93
|
Numeric.assert === 3
|
94
94
|
|
@@ -98,10 +98,10 @@ And there is *Case Equality*.
|
|
98
98
|
Because operators can not take blocks, and at times blocks can
|
99
99
|
be convenient means of supplying a value to an assertion,
|
100
100
|
AE has defined alternate renditions of the equality methods.
|
101
|
-
For equal? and eql?, the method
|
102
|
-
can take a block
|
101
|
+
For equal? and eql?, the method names are the same, they simply
|
102
|
+
can take a block in place of an argument if need be.
|
103
103
|
|
104
|
-
For
|
104
|
+
For <i>value equality</i> (<tt>==</tt>), the method is called <tt>eq?</tt>.
|
105
105
|
|
106
106
|
10.assert.eq? do
|
107
107
|
10.0
|
@@ -115,7 +115,7 @@ And should it fail...
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
-
For
|
118
|
+
For <i>case equality</i> (<tt>===</tt>), it is <tt>case?</tt>.
|
119
119
|
|
120
120
|
Numeric.assert.case? do
|
121
121
|
"3".to_i
|
@@ -142,7 +142,7 @@ in the document to verify assertion failures.
|
|
142
142
|
|
143
143
|
While testing or specifying the internal state of an object is
|
144
144
|
generally considered poor form, there are times when it is
|
145
|
-
|
145
|
+
necessary. Assert combined with +instance_eval+ makes it easy too.
|
146
146
|
|
147
147
|
class X
|
148
148
|
attr :a
|
@@ -158,14 +158,14 @@ necessay. Assert combined with +instance_eval+ makes it easy too.
|
|
158
158
|
|
159
159
|
== Catch/Try Assertions
|
160
160
|
|
161
|
-
Catch/Try throws can be tested via
|
161
|
+
Catch/Try throws can be tested via <tt>Symbol#thrown?</tt>.
|
162
162
|
|
163
163
|
:hookme.assert.thrown? do
|
164
164
|
throw :hookme
|
165
165
|
end
|
166
166
|
|
167
167
|
Alternatively, a lambda containing the potential throw
|
168
|
-
can be the receiver using
|
168
|
+
can be the receiver using <tt>throws?</tt>.
|
169
169
|
|
170
170
|
hook = lambda{ throw :hookme }
|
171
171
|
|
@@ -191,7 +191,7 @@ Ruby already provides the #nil? method.
|
|
191
191
|
|
192
192
|
nil.assert.nil?
|
193
193
|
|
194
|
-
AE
|
194
|
+
AE adds <tt>true?</tt> and <tt>false?</tt> which acts accordingly.
|
195
195
|
|
196
196
|
true.assert.true?
|
197
197
|
false.assert.false?
|
@@ -226,5 +226,38 @@ create reusable assertions.
|
|
226
226
|
|
227
227
|
"abracarba".assert palindrome
|
228
228
|
|
229
|
-
|
229
|
+
|
230
|
+
== Verifying Object State
|
231
|
+
|
232
|
+
NOTE: <i>This functionality is not currently supported, but is being
|
233
|
+
considered for a future version.</i>
|
234
|
+
|
235
|
+
If no block parameter is designated and the receiver differs from +self+
|
236
|
+
in scope of the given block, then the block is evaluated in the scope of
|
237
|
+
the receiver via +instance_eval+. This can be also be used to verify the
|
238
|
+
state of an object.
|
239
|
+
|
240
|
+
class X
|
241
|
+
attr :a
|
242
|
+
def initialize(a); @a = a; end
|
243
|
+
end
|
244
|
+
|
245
|
+
x = X.new(4)
|
246
|
+
|
247
|
+
x.must do
|
248
|
+
4 == @a
|
249
|
+
end
|
250
|
+
|
251
|
+
And should it fail...
|
252
|
+
|
253
|
+
Assertion.assert.raised? do
|
254
|
+
x.must do
|
255
|
+
5 == @a
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
For some this might be considered poor form, i.e. to test underlying
|
260
|
+
implementation. You will get no argument here. It should be used
|
261
|
+
thoughtfully, but I would not bet against there being occasions
|
262
|
+
when such validations might be handy.
|
230
263
|
|
data/demo/04_subjunctive.rdoc
CHANGED
@@ -1,51 +1,50 @@
|
|
1
1
|
= Subjunctives
|
2
2
|
|
3
|
-
Okay. I can hear the BDDers rumbling, "where's the
|
4
|
-
|
3
|
+
Okay. I can hear the BDDers rumbling, "where's the *should?*"
|
4
|
+
AE has nothing against "should", but there are different
|
5
5
|
approaches for utilizing should nomenclature in specifications,
|
6
6
|
and AE wants to be open to these techniques. One of which
|
7
|
-
|
7
|
+
is how Shoulda (http://shoulda.rubyforge.org) utilizes
|
8
8
|
+should+ in a way analogous to RSpec's use of +it+.
|
9
9
|
|
10
|
-
Even so, AE provides a an optional mixin called Subjunctive which
|
11
|
-
can be used to create assertor methods
|
12
|
-
terms such as +should
|
10
|
+
Even so, AE provides a an optional mixin called +Subjunctive+ which
|
11
|
+
can be used to create assertor methods with English subjunctive
|
12
|
+
terms, such as +should+, or +must+, +shall+ and +will+.
|
13
13
|
To load this library use:
|
14
14
|
|
15
15
|
require 'ae/subjunctive'
|
16
16
|
|
17
|
-
Then all that is required it to define
|
17
|
+
Then all that is required it to define a subjunctive method for all
|
18
18
|
objects. For example:
|
19
19
|
|
20
20
|
def will(*args, &block)
|
21
21
|
Assertor.new(self, :backtrace=>caller).be(*args,&block)
|
22
22
|
end
|
23
23
|
|
24
|
-
It's that easy. Because of their
|
25
|
-
+should+ and +must+ as optional add-ons.
|
24
|
+
It's that easy. Because of their commonality AE provides two such terms,
|
25
|
+
+should+ and +must+ as optional add-ons out-of-the-box.
|
26
26
|
|
27
27
|
require 'ae/subjunctive/should'
|
28
28
|
require 'ae/subjunctive/must'
|
29
29
|
|
30
30
|
We will use these two methods interchangeable for the rest of this
|
31
31
|
demonstration, but to be clear they both work exactly the same way,
|
32
|
-
and almost exactly like
|
32
|
+
and almost exactly like +assert+.
|
33
33
|
|
34
|
-
Keep in mind, AE "conical" functionality does not entail subjunctive
|
35
|
-
forms
|
36
|
-
|
37
|
-
or need to support these nomenclatures.
|
34
|
+
Keep in mind, AE "conical" functionality does not entail the subjunctive
|
35
|
+
forms. These are simply options you can load via your <tt>test_helper.rb</tt>,
|
36
|
+
or similar script, if you prefer these nomenclatures.
|
38
37
|
|
39
38
|
|
40
39
|
== Fluent Notation and Antonyms
|
41
40
|
|
42
|
-
Like +assert+, +should+ and +must+ can be used as
|
41
|
+
Like +assert+, +should+ and +must+ can be used as higher order functions.
|
43
42
|
|
44
43
|
4.should == 4
|
45
44
|
4.must == 4
|
46
45
|
|
47
|
-
|
48
|
-
+must
|
46
|
+
Antonyms provided for +should+ as <tt>should!</tt> (read "should not") and +shouldnt+.
|
47
|
+
For +must+ +must+, they are <tt>must!</tt> and +wont+.
|
49
48
|
|
50
49
|
4.should! == 5
|
51
50
|
4.shouldnt == 5
|
@@ -56,8 +55,8 @@ With the antonym of +should!+ (read as "should not") or +shouldnt+, and for
|
|
56
55
|
|
57
56
|
== To Be
|
58
57
|
|
59
|
-
On occasions where the English readability of a specification is
|
60
|
-
|
58
|
+
On occasions where the English readability of a specification is hindered,
|
59
|
+
+be+ can be used.
|
61
60
|
|
62
61
|
StandardError.must.be.raised? do
|
63
62
|
unknown_method
|
@@ -65,63 +64,37 @@ hindered, +be+ can be used.
|
|
65
64
|
|
66
65
|
The +be+ method is the same as +assert+ with the single exception
|
67
66
|
that it will compare a lone argument to the receiver using +equate?+,
|
68
|
-
unlike +assert+ which simply
|
67
|
+
unlike +assert+ which simply checks to see that the argument evaluates
|
69
68
|
as true.
|
70
69
|
|
71
70
|
10.should.be 10
|
72
71
|
10.should.be 10.0
|
73
72
|
10.should.be Numeric
|
74
73
|
|
74
|
+
Assertion.assert.raised? do
|
75
|
+
10.should.be "40"
|
76
|
+
end
|
77
|
+
|
75
78
|
|
76
79
|
== Indefinite Articles
|
77
80
|
|
78
|
-
|
79
|
-
that they use
|
81
|
+
Additional English forms are +a+ and +an+, equivalent to +be+ except
|
82
|
+
that they use <tt>case?</tt> (same as <tt>#===</tt>) instead of
|
83
|
+
<tt>equate?</tt> when acting on a single argument.
|
80
84
|
|
81
85
|
"hi".must.be.a String
|
82
86
|
|
83
|
-
|
87
|
+
Assertion.assert.raised? do
|
88
|
+
/x/.must.be.a /x/
|
89
|
+
end
|
84
90
|
|
85
|
-
|
91
|
+
Otherwise they are interchangeable.
|
86
92
|
|
87
|
-
|
93
|
+
"hi".must.be.an.instance_of?(String)
|
88
94
|
|
95
|
+
The indefinite articles work well when a noun follows as an arguments.
|
89
96
|
|
90
97
|
palindrome = lambda{ |x| x == x.reverse }
|
91
98
|
|
92
99
|
"abracarba".must.be.a palindrome
|
93
100
|
|
94
|
-
|
95
|
-
== Verifying Object State
|
96
|
-
|
97
|
-
The block notation of the subjunctive form is similar to +assert+, with
|
98
|
-
the important exception that the block is is evaluated in the scope of the
|
99
|
-
receiver via #instance_eval, if no block parameter is designated.
|
100
|
-
This can be also be used to test the state of an object.
|
101
|
-
|
102
|
-
class X
|
103
|
-
attr :a
|
104
|
-
def initialize(a); @a = a; end
|
105
|
-
end
|
106
|
-
|
107
|
-
x = X.new(4)
|
108
|
-
|
109
|
-
x.must do
|
110
|
-
4 == @a
|
111
|
-
end
|
112
|
-
|
113
|
-
And should it fail...
|
114
|
-
|
115
|
-
Assertion.assert.raised? do
|
116
|
-
x.must do
|
117
|
-
5 == @a
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
For some this might seem controversial --to test underlying
|
122
|
-
implementation. And you will get no argument here, it should be used
|
123
|
-
thoughtfully, but there are occasions when such validations are
|
124
|
-
necessary.
|
125
|
-
|
126
|
-
QED.
|
127
|
-
|