Mis*_*cha 5 unit-testing ruby-on-rails ruby-on-rails-3
我有一个模型UserFile
是belongs_to
一个Folder
:
class UserFile < ActiveRecord::Base
has_attached_file :attachment
belongs_to :folder
validates_attachment_presence :attachment
validates_presence_of :folder_id
def copy(target_folder)
new_file = self.clone
new_file.folder = target_folder
new_file.save!
end
end
Run Code Online (Sandbox Code Playgroud)
以下测试意外失败:
test 'cannot copy a file to anything other than a folder' do
folder = Factory(:folder)
file1 = UserFile.create(:attachment => File.open("#{Rails.root}/test/fixtures/textfile.txt"), :folder => Folder.root)
file2 = UserFile.find(file1)
# Should pass, but fails
assert_raise(ActiveRecord::RecordInvalid) { file1.copy(nil) }
# Same record, but this DOES pass
assert_raise(ActiveRecord::RecordInvalid) { file2.copy(nil) }
assert file1.copy(folder)
end
Run Code Online (Sandbox Code Playgroud)
在validates_presence_of :folder_id
使用新创建对象时被忽略,但是当我做了ActiveRecord#find
它的工作.我认为这是与调用clone
的copy
方法,但我不能弄明白.有谁知道发生了什么或如何使测试通过?
Mischa, cloning is a beast.
record.errors is memoized and the @errors instance variable gets cloned too.
file1.errors = new_file.errors
Run Code Online (Sandbox Code Playgroud)
this will be non-nil since create
called validations on file1
.
now what happens when you clone file1 and say new_file.save!
?
Deep inside valid?
calls errors.clear on new_file but it still points to the same error object as file1.
Now viciously, the presence validator is implemented like this:
def validate(record)
record.errors.add_on_blank(attributes, options)
end
Run Code Online (Sandbox Code Playgroud)
which (obviously) can only access errors.base http://apidock.com/rails/ActiveModel/Errors/add_on_blank
so, although, the validations do run on new_file as the record, the presence validation passes since
new_file.errors.instance_eval { @base } == file1
Run Code Online (Sandbox Code Playgroud)
and for file1.folder_id
is NOT blank.
Now, your second test passes because if you read the file entry from the db, file2.errors
is nil so when you clone it and call validations on the clone, the errors object is created anew with the correct base (the clone) for which folder_id
will be blank because of the line new_file.folder = target_folder
.
your problem is solved by simply adding
def copy(target_folder)
new_file = self.clone
new_file.instance_eval { @errors = nil } # forces new error object on clone
new_file.folder = target_folder
new_file.save!
end
Run Code Online (Sandbox Code Playgroud)
hope this helped
归档时间: |
|
查看次数: |
584 次 |
最近记录: |