class SqlGeneratorTest < Test::Unit::TestCaseThen, as I previously blogged about, I started using classes as stubs. But, somewhere along the way I had forgotten about OpenStruct. It wasn't until recently when a co-worker suggested he was going to write a 'stub mother' that I remembered OpenStruct.
class StubIdGenerator
def nextval; 1; end
end
def test_something
stub = StubIdGenerator.new
#some logic
end
end
OpenStruct, as the documentation states, allows you to create data objects and set arbitrary attributes. The above StubIdGenerator definition and instantiation code can be replaced by using OpenStruct with one line.
def test_somethingUnfortunately, OpenStruct does not quite behave as I would expect a stub to. For example, you could write:
stub = OpenStruct.new(:nextval=>1)
end
def test_somethingTo remedy this, I stole some of the behavior of OpenStruct and created TestStub. TestStub behaves the way I expect a stub to behave. It does not throw an exception when I call a writer that I've defined as a valid writer and returns a constant value when I call a reader. These requirements can be expressed as tests:
stub = OpenStruct.new(:nextval=>1)
stub.nextval = 2
stub.nextval # nextval now returns 2 not 1.
end
class TestStubTest < Test::Unit::TestCaseSince I was able to steal much of the behavior I needed from OpenStruct, the TestStub class was quite easy to throw together.
def test_writers_are_created_correctly
stub = TestStub.new(:bar, :baz)
assert_nothing_raised do
stub.bar = 2
stub.baz = 3
end
end
def test_calling_an_invalid_writer_raises_nme
stub = TestStub.new(:bar, :baz)
assert_raise(NoMethodError) { stub.cat = 4 }
end
def test_readers_are_created_correctly
stub = TestStub.new(:bar=>1)
assert_equal 1, stub.bar
end
def test_first_reader_when_multiple_readers_are_created_correctly
stub = TestStub.new(:bar=>1, :baz=>2)
assert_equal 1, stub.bar
end
def test_second_reader_when_multiple_readers_are_created_correctly
stub = TestStub.new(:bar=>1, :baz=>2)
assert_equal 2, stub.baz
end
def test_writers_when_readers_are_specified
stub = TestStub.new(:bar, :baz, :cat=>1)
assert_nothing_raised do
stub.bar = 2
stub.baz = 3
end
end
def test_readers_when_writers_are_specified
stub = TestStub.new(:cat, :dog, :bar=>1)
assert_equal 1, stub.bar
end
end
class TestStub
def initialize(*should_respond_to)
@table = {}
should_respond_to.each do |item|
meta = class << self; self; end
create_readers(meta, item) and next if item.kind_of? Hash
create_writer(meta, item) and next if item.kind_of? Symbol
end
end
def create_readers(meta, item)
item.each_pair do |key, val|
@table[key.to_sym] = val
meta.send(:define_method, key.to_sym) { @table[key.to_sym] }
end
end
def create_writer(meta, item)
meta.send(:define_method, :"#{item}=") { }
end
attr_reader :table # :nodoc:
protected :table
#
# Compare this object and +other+ for equality.
#
def ==(other)
return false unless(other.kind_of?(TestStub))
return @table == other.table
end
end
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.