Отличный способ проверки и тестирования Rails 3 ассоциаций (с использованием RSpec/Remarkable)?
Я все еще довольно новичок в тестировании в Rails 3, и я использую RSpec и Remarkable. Я прочитал много сообщений и некоторых книг уже, но я все еще немного застрял в неопределенности, когда использовать имя ассоциации, когда его ID.
class Project < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :project
end
Из-за хорошей практики я хочу защитить свои атрибуты от массовых назначений:
class Task < ActiveRecord::Base
attr_accessible :project # Or is it :project_id??
belongs_to :project
end
Прежде всего, я хочу убедиться, что существует проект никогда без действительной задачи:
class Task < ActiveRecord::Base
validates :project, :presence => true # Which one is the...
validates :project_id, :presence => true # ...right way to go??
end
Я также хочу убедиться, что присвоенный идентификатор проекта или проекта всегда действителен:
class Task < ActiveRecord::Base
validates :project, :associated => true # Again, which one is...
validates :project_id, :associated => true # ...the right way to go?
end
... и мне нужна проверка: наличие, когда я использую: связанный
Большое спасибо за разъяснение, кажется, что после нескольких часов чтения и попытки проверить материал с помощью RSpec/Shoulda/Remarkable я больше не вижу лес из-за всех деревьев...
Ответы
Ответ 1
Кажется, это правильный способ:
attr_accessible :project_id
Вам тоже не нужно ставить :project
! В любом случае, можно сделать task.project=(Project.first!)
Затем проверьте существование :project_id
, используя следующее (:project_id
также устанавливается, когда используется task.project=(...)
):
validates :project_id, :presence => true
Теперь убедитесь, что соответствующий проект действителен следующим образом:
validates :project, :associated => true
Итак:
t = Task.new
t.project_id = 1 # Value is accepted, regardless whether there is a Project with ID 1
t.project = Project.first # Any existing valid project is accepted
t.project = Project.new(:name => 'valid value') # A new valid project is accepted
t.project = Project.new(:name => 'invalid value') # A new invalid (or an existing invalid) project is NOT accepted!
Очень жаль, что при назначении идентификатора через t.project_id =
он не проверял, действительно ли этот конкретный идентификатор существует. Вы должны проверить это с помощью специальной проверки или с помощью Проверяет наличие GEM.
Чтобы проверить эти ассоциации с помощью RSpec с замечательными совпадениями, сделайте что-то вроде:
describe Task do
it { should validate_presence_of :project_id }
it { should validate_associated :project }
end
Ответ 2
validates :project, :associated => true
validates :project_id, :presence => true
Если вы хотите быть уверенным, что существует ассоциация, вам нужно проверьте, присутствует ли внешний ключ, используемый для сопоставления ассоциации, а не связанный с ним объект. http://guides.rubyonrails.org/active_record_validations_callbacks.html
attr_accessible :project_id
Ответ 3
EDIT: предполагается, что ассоциация не является необязательной...
Единственный способ, с помощью которого я могу его полностью проверить, заключается в следующем:
validates_associated :project
validates_presence_of :project_id,
:unless => Proc.new {|o| o.project.try(:new_record?)}
validates_presence_of :project, :if => Proc.new {|o| o.project_id}
Первая строка проверяет, является ли соответствующий проект действительным, если он есть. Вторая строка настаивает на наличии project_id
, если только соответствующий проект не существует и не является новым (если это новая запись, у него еще не будет ID). Третья строка гарантирует, что проект присутствует, если присутствует идентификатор, т.е. Если связанный проект уже сохранен.
ActiveRecord назначит project_id
задаче, если назначить сохраненный проект на project
. Если вы присвойте несохраненный/новый проект project
, он оставит project_id
пустым. Таким образом, мы хотим убедиться, что project_id
присутствует, но только при работе с сохраненным проектом; это выполняется в первой строке выше.
И наоборот, если вы присвоите номер project_id
, который представляет реальный проект, ActiveRecord заполнит project
соответствующим объектом Project. Но если присвоенный вами идентификатор является фиктивным, он оставит project
равным нулю. Таким образом, строка три выше, что гарантирует, что мы имеем project
, если project_id
заполняется - если вы укажете фиктивный идентификатор, это не удастся.
См. примеры RSpec, которые проверяют эти проверки: https://gist.github.com/kianw/5085085
Ответ 4
Решение Joshua Muheim работает, но я ненавижу, что не могу просто связать проект с задачей с таким идентификатором:
t = Task.new
t.project_id = 123 # Won't verify if it valid or not.
Итак, я придумал это:
class Task < ActiveRecord:Base
belongs_to :project
validates :project_id, :presence => true
validate :project_exists
private
def project_exists
# Validation will pass if the project exists
valid = Project.exists?(self.project_id)
self.errors.add(:project, "doesn't exist.") unless valid
end
end