2010-09-26 36 views
2

在Rails(3.0)測試代碼中,我克隆了一個對象,因此我可以在不更改原始的情況下對其進行驗證測試。如果我在克隆之前調用了assert(original.valid?),那麼即使在我將member_id值設置爲nil之後,克隆也會通過validates_presence_of測試。Rails3:克隆已驗證的對象可以防止克隆無效 - 這是奇怪還是正常?

下面的兩個測試說明了這一點。在測試一中,在之前創建了克隆,原始(「聯繫」)被驗證。當member_id缺失時,克隆正確地失敗了驗證。斷言C成功。

在測試二中,克隆創建後原始驗證。即使clone.member_id設置爲零,它通過驗證。換句話說,斷言2C失敗。在測試之間的唯一差異是兩條線的順序:

cloned = contact.clone 
    assert(contact.valid?,"A") 

這到底是怎麼回事?這是正常的Ruby行爲:克隆我只是不明白?

test "clone problem 1" do 
    contact = Contact.new(:member_id => 1) 
    cloned = contact.clone 
    assert(contact.valid?,"A") 
    cloned.member_id = nil 
    assert(!cloned.valid?,"C") 
end 

test "clone problem 2" do 
    contact = Contact.new(:member_id => 1) 
    assert(contact.valid?,"2A") 
    cloned = contact.clone 
    cloned.member_id = nil 
    assert(!cloned.valid?,"2C") 
end 
+0

我忘了提及,在測試2中,我使用assert(!cloned.member_id)檢查以確保cloned.member_id在賦值之後爲零。它是零,但仍然通過驗證。 – 2010-09-26 23:01:11

回答

3

你一定會驚訝 - 它不能工作!

好的原因可以在Rails代碼中找到。首先驗證將運行代碼:

# Validations module 

# Returns the Errors object that holds all information about 
# attribute error messages. 
def errors 
    @errors ||= Errors.new(self) 
end 

由於這是第一次運行,它將創建Errors類的新實例。簡單,不是嗎?但是有一個問題 - 參數是自我。在你的情況下,它是「聯繫」對象。

然後,當您在克隆的對象上再次調用時,不會再次創建@errors實例 - 因爲它不爲空。在那裏!而不是通過「克隆」自我,使用舊的自我。

後來在驗證代碼中,Errors類運行從@base讀取值的代碼,它是初始化時的self。你能看見它嗎?測試值是從原始模型中讀取的,而不是從克隆中讀取的!所以「克隆」對象的驗證運行在原始值上。好吧,到目前爲止「爲什麼不」,現在幾句關於「如何」。

解決方案很簡單 - 只需在克隆和驗證之前將@errors設置爲零即可。由於它非常私密,簡單的分配不起作用。但這個工程:

cloned.instance_eval do 
    @errors = nil 
end 

而且一些尖端有趣的閱讀:http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/

這是很全面的解釋如何在Rails 3件的作品驗證。

+0

謝謝,我將不得不真的想一想,但無疑我會在這個過程中學到一些東西。我太習慣於簡單的程序語言,有時物體的微妙之處完全讓我感動。 – 2010-09-27 06:51:38