2012-10-31 59 views
3

我想在Ruby中一個類的兩個實例的單元測試千篇一律:如何在Ruby中對類實例的相同/相等進行單元測試?

def test_example 
    a = Object.new 
    b = Object.new 

    assert_equal a, b 
end 

我明白,這將失敗,因爲情況是不同的變量,每個都有自己的內存指針。我所追求的是如果實例在所有方面都相同但是它們的引用指針通過此測試通過。

下面是一個更復雜的(如果做作)例如:

# 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 
+0

Victor Deryagin的回答是正確的。我建議你還通讀http://www.skorks.com/2009/09/ruby-equality-and-object-comparison/,因爲它包含一些更有趣的信息... – severin

回答

1
f1.attributes.keys.collect(&:to_sym).each do |field| 
assert_equal f1.send(field), f2.send(field) 
end 

這將宣稱所有領域的平等。但缺點是斷言的數量。如果你不希望這樣的事情發生,分配ID的對象這樣

f1.id = 1 
f2.id = 1 
assert_equal f1, f2 

但一定不救可能導致不一致的對象。

+0

'屬性'方法僅適用於ActiveRecord模型。 ActiveRecord已經重新定義了平等,只考慮記錄的ID而不是它的object_id ... – severin

+0

好了!使用第一個。問題是積極記錄的權利?那裏有什麼問題? –

+0

問題是香草紅寶石,使用Test :: Unit標準庫,雖然與另一個測試框架(RSpec,Shoulda)的有效答案也可以接受。 –

-1

根據在apidock處的來源,assert_equals首先使用inspect方法將對象轉換爲字符串。你應該定義的檢查方法爲class FooBar

class FooBar 
    def inspect 
    # create a unique string of attributes here, maybe a json string. 
    end 
end 
+0

這將是'assert_equals'中的一個弱點。正如Victor Deryagin在他的回答中指出的那樣,正確的方法是實現/重寫'=='。 – severin

1

只要定義FooBar#==方法:

class FooBar 
    def ==(other) 
    [bar, seed, foo] == [other.bar, other.seed, other.foo] 
    end 
end 

現在test_foobar_1_inittest_foobar_2_instance_var通,test_foobar_3_diff失敗了正當的理由。

不利的一面是當你改變對象結構時,==方法需要相應修改。

+0

感謝您的回覆,維克多。但是,手動檢查所有屬性的相等性正是我想要避免的。必須有更好的方法...另外,這並不處理比較方法的存在。 (例如,一個實例可以注入一個新的方法,FooBar#==方法不會知道)。 –

相關問題