Reputation: 5618
I'm trying to unit test sameness of two instances of one class in Ruby:
def test_example
a = Object.new
b = Object.new
assert_equal a, b
end
I understand that this fails because the instances are distinct variables each with its own memory pointer. What I'm after is to get this test to pass if the instances are identical in all respects but their reference pointers.
Here's a more involved (if contrived) example:
# Let's stir up something...
class FooBar
attr_accessor :seed, :foo, :bar
def foo_the_bar()
@bar = @seed + @foo * 3
end
end
f = FooBar.new
f.seed = "Mountains "
f.foo = "are just mountains "
f.bar = "obliterate me!"
f.foo_the_bar
p f.bar # "Mountains are just mountains are just mountains are just mountains "
# So far so good, now let's test if one FooBar is same as another...
require 'test/unit'
class FooBarTest < Test::Unit::TestCase
# Test fails: <#<FooBar:0x9a40d18>> expected but was <#<FooBar:0x9a40d04>>.
# But what we really want is a way to make this pass
# since the objects are exactly the same in every respect,
# besides their pointers.
def test_foobar_1_init
f1 = FooBar.new
f2 = FooBar.new
assert_equal f1, f2
end
# Again, test fails, but we want it to pass,
# since the instance variables are the same.
def test_foobar_2_instance_var
f1 = FooBar.new
f2 = FooBar.new
f1.seed = "Santa says "
f1.foo = "ho "
f1.bar = "something"
f1.foo_the_bar
f2.seed = f1.seed
f2.foo = f1.foo
f2.foo_the_bar
assert_equal f1, f2
end
# This test fails for the wrong reason.
# We want this to fail because the instance variables are different.
# This test should also fail if we add a method to one of the instances,
# or make the instances differ from each some other way.
def test_foobar_3_diff
f1 = FooBar.new
f2 = FooBar.new
f1.seed = "Kitty goes "
f1.foo = "meow "
f1.foo_the_bar
f2.seed = "Doggies goes "
f2.foo = "woof "
f2.foo_the_bar
assert_equal f1, f2
end
end
Upvotes: 4
Views: 747
Reputation: 12225
Just define FooBar#==
method:
class FooBar
def ==(other)
[bar, seed, foo] == [other.bar, other.seed, other.foo]
end
end
now test_foobar_1_init
and test_foobar_2_instance_var
pass, test_foobar_3_diff
fails for the right reason.
A downside is when you change object structure, ==
method needs to be modified accordingly.
Upvotes: 1
Reputation: 710
As per the source at apidock, assert_equals first converts objects to string using inspect
method. You should define the inspect method for the class FooBar
class FooBar
def inspect
# create a unique string of attributes here, maybe a json string.
end
end
Upvotes: -1
Reputation: 2950
f1.attributes.keys.collect(&:to_sym).each do |field|
assert_equal f1.send(field), f2.send(field)
end
This will assert equality of all the fields. But the downside is the number of assertions. If you dont want that to happen, assign ids to the objects like this
f1.id = 1
f2.id = 1
assert_equal f1, f2
But be sure not to save the objects which might lead to inconsistencies.
Upvotes: 1