previous next
| Description | Result of running test |
|---|
This is a Warts and All demonstration of Test Driven Development of
a Universal Unit Test. It was not pair programmed. Each snapshot is
taken at "it compiles & runs without warnings" stage.
The green lines of code is the text added since the last snapshot, the blue strikeout text is the text deleted.
The red/green box just to the right of this text is the result of running the unit test for the UUT. green == passed, red === failed.
Start off with a generic TDD stub.
The mystik...
if $0 == __FILE__ then
...line is a standard ruby idiom, it you run _this_ file, it assumes you are wanting to run the unit test for the class declared in this file. If you use this module in some other file, the part after that line won't be run. ie. You could pull the UUT class into the unit test of another class.
| Loaded suite uut
Started
.
Finished in 0.000535 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
#Generic TDD stub
class UUT
def initialize()
end
end
if $0 == __FILE__ then
require 'test/unit'
class TC_Uut < Test::Unit::TestCase
def test_uut
end
end
end
previous next
| Start with minimal Universal Unit Test, that does nothing, and hence
no surprise, it passes!
| Loaded suite uut
Started
.
Finished in 0.000561 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
#Generic TDD stub
class UUT
def initialize()
end
def test( forward, backward)
end
end
class TransformationTrait
end
if $0 == __FILE__ then
require 'test/unit'
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = TransformationTrait.new
backward = TransformationTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Make our UUT try every sample, transform it forward and back, and
compare.
Since the transform does nothing, and the fuzzy_equal? always returns
true, the test passes.
| Loaded suite uut
Started
.
Finished in 0.001193 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
true
end
end
class UUT
def initialize()
end
def test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward and back sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
end
end
class TransformationTrait
end
if $0 == __FILE__ then
require 'test/unit'
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = TransformationTrait.new
backward = TransformationTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Add a new transform trait that squares the input samples.
Since the fuzzy equals is still stupid, it still passes.
| Loaded suite uut
Started
.
Finished in 0.00125 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
true
end
end
class UUT
def initialize()
end
def test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward and back sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
end
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
sample * sample
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = TransformationTrait.new
forward = SqrTrait.new
backward = TransformationTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Make fuzzy_equal? more real, so test fails. (I actually chose the
wrong variety of equal? here. A pair programmer may have pick that up. However,
the bug shows up later and I fix it later.)
| Loaded suite uut
Started
E
Finished in 0.00077 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Forward and back sample differ
uut.rb:26:in `test'
uut.rb:23:in `each'
uut.rb:23:in `test'
uut.rb:50:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
true
a.equal?( b)
end
end
class UUT
def initialize()
end
def test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward and back sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
end
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
sample * sample
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = TransformationTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Added the simplest and stupidest implementation of sqrt. (return 2)
Throw an input range exception if the input value is not 4.
| Loaded suite uut
Started
uut.rb:47:in `transform': InputRangeExceptionValue '1' out of input range for transformation (InputRangeException)
from uut.rb:35:in `test'
from uut.rb:34:in `each'
from uut.rb:34:in `test'
from uut.rb:68:in `test_uut'
from /usr/lib/ruby/1.8/test/unit/testcase.rb:70:in `__send__'
from /usr/lib/ruby/1.8/test/unit/testcase.rb:70:in `run'
from /usr/lib/ruby/1.8/test/unit/testsuite.rb:32:in `run'
from /usr/lib/ruby/1.8/test/unit/testsuite.rb:31:in `each'
... 8 levels...
from /usr/lib/ruby/1.8/test/unit/autorunner.rb:200:in `run'
from /usr/lib/ruby/1.8/test/unit/autorunner.rb:13:in `run'
from /usr/lib/ruby/1.8/test/unit.rb:285
from /usr/lib/ruby/1.8/test/unit.rb:283
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward and back sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
end
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
sample * sample
end
end
class SqrtTrait < TransformationTrait
def transform( sample)
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = TransformationTrait.new
uut.test( forward, backward)
end
end
end
previous next
| So the forward transform throws an input range error. Good. It should
be able to. Ignore it and drop it on the floor.
Now we're past that and it the fuzzy compare is whinging. (But we don't know why...)
| Loaded suite uut
Started
E
Finished in 0.000803 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Forward and back sample differ
uut.rb:42:in `test'
uut.rb:34:in `each'
uut.rb:34:in `test'
uut.rb:73:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
reverted_sample = backward.transform( transformed_sample)
raise "Forward and back sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
end
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
sample * sample
end
end
class SqrtTrait < TransformationTrait
def transform( sample)
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = TransformationTrait.new
uut.test( forward, backward)
end
end
end
previous next
| So print out a bit more info... | Loaded suite uut
Started
E
Finished in 0.000804 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Forward '1' and back '' sample differ
uut.rb:42:in `test'
uut.rb:34:in `each'
uut.rb:34:in `test'
uut.rb:73:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
reverted_sample = backward.transform( transformed_sample)
raise "Forward and back sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
end
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
sample * sample
end
end
class SqrtTrait < TransformationTrait
def transform( sample)
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = TransformationTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Ah! That rescue is in the wrong place. Drat! Still wrong... | Loaded suite uut
Started
E
Finished in 0.000915 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Forward '4' and back '16' sample differ
uut.rb:38:in `test'
uut.rb:34:in `each'
uut.rb:34:in `test'
uut.rb:72:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
reverted_sample = backward.transform( transformed_sample)
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
end
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
sample * sample
end
end
class SqrtTrait < TransformationTrait
def transform( sample)
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = TransformationTrait.new
uut.test( forward, backward)
end
end
end
previous next
| It helps if you actually use the SqrtTrait you defined. :-) Still wrong, but I can see what is wrong.. | Loaded suite uut
Started
E
Finished in 0.001878 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Forward '4' and back '2' sample differ
uut.rb:38:in `test'
uut.rb:34:in `each'
uut.rb:34:in `test'
uut.rb:72:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
sample * sample
end
end
class SqrtTrait < TransformationTrait
def transform( sample)
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = TransformationTrait.new
backward = SqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Should have used 2 not 4. Pair programming would be a Good idea... | Loaded suite uut
Started
.
Finished in 0.004899 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
raise InputRangeException.new( sample) unless sample == 2
sample * sample
end
end
class SqrtTrait < TransformationTrait
def transform( sample)
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = SqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| So that was a really dumb implementation of sqrt, called it so instead
of deleting it. (I'm sort of attached to it, I hate to lose code that
sort of does something sane.)
| Loaded suite uut
Started
.
Finished in 0.00513 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 2
sample * sample
end
end
class SqrtTrait < TransformationTrait
class DumbSqrtTrait < TransformationTrait
def transform( sample)
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = SqrtTrait.new
backward = DumbSqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
Wake up!
Important step here! Which way is forward and which is back? Who cares, it should work both ways. Oh dear! It doesn't.
| Loaded suite uut
Started
E
Finished in 0.00518 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Forward '1' and back '4' sample differ
uut.rb:38:in `one_way_test'
uut.rb:34:in `each'
uut.rb:34:in `one_way_test'
uut.rb:47:in `test'
uut.rb:77:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def test( forward, backward)
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 2
sample * sample
end
end
class DumbSqrtTrait < TransformationTrait
def transform( sample)
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = DumbSqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Just need to constrain the range for this dumbsqrt thing again. | Loaded suite uut
Started
.
Finished in 0.009366 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 2
sample * sample
end
end
class DumbSqrtTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = DumbSqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Hmm. But that constraint is really a property of the dumb implementation of sqrt, it shouldn't even be on the SqrTrait.
Ooh. I didn't need it...
| Loaded suite uut
Started
.
Finished in 0.010423 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
reverted_sample = backward.transform( transformed_sample)
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 2
sample * sample
end
end
class DumbSqrtTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = DumbSqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| That's because I have the rescue over the forward and back transformation. It should only be over the forward. ie. The output of the the forward transform should always be OK for the input of the backward.
That's better, the test breaks again.
| Loaded suite uut
Started
E
Finished in 0.000815 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Unit Test failure, output range of transformation larger
than input range of reverse - 'InputRangeExceptionValue '1' out of input range for transformation'
uut.rb:42:in `one_way_test'
uut.rb:34:in `each'
uut.rb:34:in `one_way_test'
uut.rb:54:in `test'
uut.rb:85:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
begin
reverted_sample = backward.transform( transformed_sample)
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException => details
raise "Unit Test failure, output range of transformation larger
than input range of reverse - '#{details}'"
end
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless
forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
sample * sample
end
end
class DumbSqrtTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = DumbSqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Make sample_generator and attribute of the trait so I can tamper with it. (test still broken - good.) | Loaded suite uut
Started
E
Finished in 0.000824 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Unit Test failure, output range of transformation larger
than input range of reverse - 'InputRangeExceptionValue '1' out of input range for transformation'
uut.rb:44:in `one_way_test'
uut.rb:36:in `each'
uut.rb:36:in `one_way_test'
uut.rb:56:in `test'
uut.rb:87:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
def sample_generator
(1..100)
attr_accessor :sample_generator
def initialize
@sample_generator = (1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
begin
reverted_sample = backward.transform( transformed_sample)
rescue InputRangeException => details
raise "Unit Test failure, output range of transformation larger
than input range of reverse - '#{details}'"
end
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless
forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
sample * sample
end
end
class DumbSqrtTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
backward = DumbSqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Constrain the forward sample generator to generate just one and only one sample. 2. | Loaded suite uut
Started
.
Finished in 0.00503 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
attr_accessor :sample_generator
def initialize
@sample_generator = (1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
begin
reverted_sample = backward.transform( transformed_sample)
rescue InputRangeException => details
raise "Unit Test failure, output range of transformation larger
than input range of reverse - '#{details}'"
end
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless
forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
sample * sample
end
end
class DumbSqrtTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
forward.sample_generator = [2]
backward = DumbSqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Put in a real implementation of Sqrt and the test breaks. It breaks because I used the wrong version of equal? but I didn't notice that yet. I assumed it's due to floating point arithmetic. Twit. | Loaded suite uut
Started
E
Finished in 0.005217 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Forward '1' and back '1.0' sample differ
uut.rb:47:in `one_way_test'
uut.rb:36:in `each'
uut.rb:36:in `one_way_test'
uut.rb:56:in `test'
uut.rb:97:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
attr_accessor :sample_generator
def initialize
@sample_generator = (1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
begin
reverted_sample = backward.transform( transformed_sample)
rescue InputRangeException => details
raise "Unit Test failure, output range of transformation larger
than input range of reverse - '#{details}'"
end
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless
forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
def transform( sample)
sample * sample
end
end
class DumbSqrtTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
2
end
end
class SqrtTrait < TransformationTrait
def transform( sample)
Math.sqrt( sample)
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
forward.sample_generator = [2]
backward = DumbSqrtTrait.new
uut.test( forward, backward)
forward = SqrTrait.new
backward = SqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Put in a floating point fuzzy equal, things work. Cool. | Loaded suite uut
Started
.
Finished in 0.007402 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
attr_accessor :sample_generator
def initialize
@sample_generator = (1..100)
end
def transform( sample)
sample
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class FloatingPointTransformationTrait < TransformationTrait
def fuzzy_equal?( a, b)
z = (a.abs + b.abs) / 2.0
((a - b).abs / z) < 0.0001
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
begin
reverted_sample = backward.transform( transformed_sample)
rescue InputRangeException => details
raise "Unit Test failure, output range of transformation larger
than input range of reverse - '#{details}'"
end
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless
forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < TransformationTrait
class SqrTrait < FloatingPointTransformationTrait
def transform( sample)
sample * sample
end
end
class DumbSqrtTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
2
end
end
class SqrtTrait < TransformationTrait
class SqrtTrait < FloatingPointTransformationTrait
def transform( sample)
Math.sqrt( sample)
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
forward.sample_generator = [2]
backward = DumbSqrtTrait.new
uut.test( forward, backward)
forward = SqrTrait.new
backward = SqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| I'm starting to get a clutter of Traits. Let's refactor and clean up. | Loaded suite uut
Started
.
Finished in 0.00842 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
attr_accessor :sample_generator
def initialize
def initialize( transform = Proc.new{|sample| sample} )
@sample_generator = (1..100)
@transform = transform
end
def transform( sample)
sample
@transform.call(sample)
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class FloatingPointTransformationTrait < TransformationTrait
def fuzzy_equal?( a, b)
z = (a.abs + b.abs) / 2.0
((a - b).abs / z) < 0.0001
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
begin
reverted_sample = backward.transform( transformed_sample)
rescue InputRangeException => details
raise "Unit Test failure, output range of transformation larger
than input range of reverse - '#{details}'"
end
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless
forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class SqrTrait < FloatingPointTransformationTrait
def transform( sample)
sample * sample
end
end
class DumbSqrtTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
2
end
end
class SqrtTrait < FloatingPointTransformationTrait
def transform( sample)
Math.sqrt( sample)
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = SqrTrait.new
forward = FloatingPointTransformationTrait.new( Proc.new{|s| s*s})
forward.sample_generator = [2]
backward = DumbSqrtTrait.new
uut.test( forward, backward)
forward = SqrTrait.new
forward = FloatingPointTransformationTrait.new( Proc.new{|s| s*s})
backward = SqrtTrait.new
uut.test( forward, backward)
end
end
end
previous next
| Grreat. That worked. Let's clobber another. I know I'm on the right
track when by deleting code I'm adding functionality...
| Loaded suite uut
Started
.
Finished in 0.009252 seconds.
1 tests, 0 assertions, 0 failures, 0 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
attr_accessor :sample_generator
def initialize( transform = Proc.new{|sample| sample} )
@sample_generator = (1..100)
@transform = transform
end
def transform( sample)
@transform.call(sample)
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class FloatingPointTransformationTrait < TransformationTrait
def fuzzy_equal?( a, b)
z = (a.abs + b.abs) / 2.0
((a - b).abs / z) < 0.0001
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
begin
reverted_sample = backward.transform( transformed_sample)
rescue InputRangeException => details
raise "Unit Test failure, output range of transformation larger
than input range of reverse - '#{details}'"
end
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless
forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class DumbSqrtTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
2
end
end
class SqrtTrait < FloatingPointTransformationTrait
def transform( sample)
Math.sqrt( sample)
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = FloatingPointTransformationTrait.new( Proc.new{|s| s*s})
forward.sample_generator = [2]
backward = DumbSqrtTrait.new
uut.test( forward, backward)
forward = FloatingPointTransformationTrait.new( Proc.new{|s| s*s})
backward = SqrtTrait.new
backward = FloatingPointTransformationTrait.new( Proc.new{|s| Math.sqrt(s)})
uut.test( forward, backward)
end
end
end
previous next
| But really. My testing is very weak. Let's do some real
testing. Malicious samples, systematic samples and random
samples. Let's break stuff.
Ooo - it broke. Ah. Well. 0 is a fairly malicious sort of number.
| Loaded suite uut
Started
E
Finished in 0.005381 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Forward '0.0' and back '0.0' sample differ
uut.rb:102:in `one_way_test'
uut.rb:91:in `malicious_samples'
uut.rb:55:in `each'
uut.rb:91:in `one_way_test'
uut.rb:111:in `test'
uut.rb:140:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
attr_accessor :sample_generator
def initialize( transform = Proc.new{|sample| sample} )
@sample_generator = (1..100)
@transform = transform
end
def transform( sample)
@transform.call(sample)
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class FloatingPointSampleGenerator
def initialize( n_random_samples = 200, n_systematic = 200)
@n_random_samples = n_random_samples
@n_systematic = n_systematic
end
def random_samples
(0...@n_random_samples).each do |i|
r = rand * 10.0 - 5.0
# Cube it.
r *= r * r
yield r
end
end
def systematic_samples
x = -100.0
step = x / (@n_systematic * 2.0)
for i in (0...@n_systematic)
yield x
x += step
end
end
def each( &block)
malicious_samples(&block)
systematic_samples(&block)
random_samples( &block)
end
def malicious_samples
yield 0.0
yield( -1.0)
yield( -2.0)
yield 1.0
yield 2.0
yield 100.0
yield 0.5
yield( -0.5)
end
end
class FloatingPointTransformationTrait < TransformationTrait
def initialize( transform = Proc.new{|sample| sample} )
super( transform)
@sample_generator = FloatingPointSampleGenerator.new
end
def fuzzy_equal?( a, b)
z = (a.abs + b.abs) / 2.0
((a - b).abs / z) < 0.0001
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
begin
reverted_sample = backward.transform( transformed_sample)
rescue InputRangeException => details
raise "Unit Test failure, output range of transformation larger
than input range of reverse - '#{details}'"
end
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless
forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end
end
def test( forward, backward)
one_way_test( forward, backward)
one_way_test( backward, forward)
end
end
if $0 == __FILE__ then
require 'test/unit'
class DumbSqrtTrait < TransformationTrait
def transform( sample)
raise InputRangeException.new( sample) unless sample == 4
2
end
end
class TC_Uut < Test::Unit::TestCase
def test_uut
uut = UUT.new
# Either the transformation objects need to be quite smart,
# or we must pass in a Trait class that knows stuff.
# I like the idea of a Trait class.
forward = FloatingPointTransformationTrait.new( Proc.new{|s| s*s})
forward.sample_generator = [2]
backward = DumbSqrtTrait.new
uut.test( forward, backward)
forward = FloatingPointTransformationTrait.new( Proc.new{|s| s*s})
backward = FloatingPointTransformationTrait.new( Proc.new{|s| Math.sqrt(s)})
uut.test( forward, backward)
end
end
end
previous next
| Let's do that fuzzy equal properly.
Dang! Still broke. Ah yes! Squaring a number is a lossy transformation, it loses all info about the sign of the number.
| Loaded suite uut
Started
E
Finished in 0.005358 seconds.
1) Error:
test_uut(TC_Uut):
RuntimeError: Forward '-1.0' and back '1.0' sample differ
uut.rb:105:in `one_way_test'
uut.rb:94:in `malicious_samples'
uut.rb:55:in `each'
uut.rb:94:in `one_way_test'
uut.rb:114:in `test'
uut.rb:143:in `test_uut'
1 tests, 0 assertions, 0 failures, 1 errors
|
class InputRangeException < Exception
def initialize( value)
@value = value
end
def to_s
super + "Value '#{@value}' out of input range for transformation"
end
end
class TransformationTrait
attr_accessor :sample_generator
def initialize( transform = Proc.new{|sample| sample} )
@sample_generator = (1..100)
@transform = transform
end
def transform( sample)
@transform.call(sample)
end
def fuzzy_equal?( a, b)
a.equal?( b)
end
end
class FloatingPointSampleGenerator
def initialize( n_random_samples = 200, n_systematic = 200)
@n_random_samples = n_random_samples
@n_systematic = n_systematic
end
def random_samples
(0...@n_random_samples).each do |i|
r = rand * 10.0 - 5.0
# Cube it.
r *= r * r
yield r
end
end
def systematic_samples
x = -100.0
step = x / (@n_systematic * 2.0)
for i in (0...@n_systematic)
yield x
x += step
end
end
def each( &block)
malicious_samples(&block)
systematic_samples(&block)
random_samples( &block)
end
def malicious_samples
yield 0.0
yield( -1.0)
yield( -2.0)
yield 1.0
yield 2.0
yield 100.0
yield 0.5
yield( -0.5)
end
end
class FloatingPointTransformationTrait < TransformationTrait
def initialize( transform = Proc.new{|sample| sample} )
super( transform)
@sample_generator = FloatingPointSampleGenerator.new
end
EPSILON = 0.0001
def fuzzy_equal?( a, b)
z = (a.abs + b.abs) / 2.0
((a - b).abs / z) < 0.0001
return true if z < EPSILON
((a - b).abs / z) < EPSILON
end
end
class UUT
def initialize()
end
def one_way_test( forward, backward)
samples = forward.sample_generator
samples.each do |sample|
begin
transformed_sample = forward.transform( sample)
begin
reverted_sample = backward.transform( transformed_sample)
rescue InputRangeException => details
raise "Unit Test failure, output range of transformation larger
than input range of reverse - '#{details}'"
end
raise "Forward '#{sample}' and back '#{reverted_sample}' sample differ" unless
forward.fuzzy_equal?( sample, reverted_sample)
rescue InputRangeException
# Ignore input range exceptions here...
end
end