2012-07-04 7 views
17

Ich bin immer noch ziemlich neu in Rails 3 zu testen, und ich verwende RSpec und Remarkable. Ich habe schon viele Posts und Bücher durchgelesen, aber ich bin irgendwie immer noch in Ungewissheit, wann ich den Namen des Vereins benutzen soll, wenn ich seine ID habe.Die perfekte Methode, um Rails 3-Assoziationen zu validieren und zu testen (mit RSpec/Remarkable)?

class Project < ActiveRecord::Base 
    has_many :tasks 
end 

class Task < ActiveRecord::Base 
    belongs_to :project 
end 

Wegen der guten Praxis, ich möchte, dass meine Attribute von Massenzuweisungen schützen:

class Task < ActiveRecord::Base 
    attr_accessible :project # Or is it :project_id?? 

    belongs_to :project 
end 

Zunächst einmal möchte ich sicherstellen, dass ein Projekt nie ohne gültige Aufgabe besteht:

class Task < ActiveRecord::Base 
    validates :project, :presence => true  # Which one is the... 
    validates :project_id, :presence => true # ...right way to go?? 
end 

ich möchte auch darauf achten, dass das zugeordnete Projekt oder Projekt-ID ist immer gültig:

... und brauche ich die Validierung auf: Präsenz, wenn ich verwende: verbunden ??

vielen Dank für die Klärung, so scheint es, dass RSpec/Shoulda/Bemerkenswerte ich den Wald nicht sehen, weil alle Bäume nach Stunden des Lesens und versuchen, Sachen zu testen, mit mehr ...

+0

Schöne, klare Frage. Um zu bestätigen, meinst du nicht, du willst "sicherstellen, dass eine Aufgabe ** niemals ** ohne ein gültiges (Eltern-) Projekt existiert"? –

Antwort

12

Dies scheint der richtige Weg zu sein, es zu tun:

attr_accessible :project_id 

Sie müssen :project dort nicht setzen, auch! Es ist auf jeden Fall möglich task.project=(Project.first!)

Dann sind Sie für die Existenz des :project_id mit dem folgenden (:project_id wird auch festgelegt, wenn task.project=(...) verwendet wird) zu tun:

validates :project_id, :presence => true 

jetzt sicher, als ein zugehöriges Projekt wie dieses gültig ist :

validates :project, :associated => true 

So:

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! 

Es ist ein bisschen schade, dass bei der Zuweisung einer ID über t.project_id = nicht geprüft wird, ob diese spezifische ID wirklich existiert. Sie müssen dies mithilfe einer benutzerdefinierten Überprüfung oder mithilfe der Validates Existence GEM überprüfen.

diese Assoziationen mit RSpec mit bemerkenswerten Matcher Um zu testen, so etwas wie:

describe Task do 
    it { should validate_presence_of :project_id } 
    it { should validate_associated :project } 
end 
+2

Wow, das ist ein Augenöffner. Und ich bekomme unerwartete Ergebnisse mit Rails 3.2.3. Versuche dies. Fügen Sie im Task-Modell "validates: project,: presentation => true", aber keine ': associated'-Validierung hinzu. Erstellen Sie in der Konsole eine neue Aufgabe mit project_id = 999 und prüfen Sie, ob es '.valid?' Ist. Rails sucht dann in der projects-Tabelle nach dem Datensatz (vermutlich, weil es nicht gefunden wird, und project_id ist immernoch null), schlägt die Validierung fehl. Ändern Sie nun die Validierung in "validates: project_id,: presentation => true". Rails liest keine Projekte und die Validierung ist erfolgreich. Fügen Sie ': associated' hinzu und die Validierung * still * ist erfolgreich. –

4
validates :project, :associated => true 
validates :project_id, :presence => true 

Wenn Sie sicher sein möchten, dass eine Verknüpfung vorhanden ist, müssen Sie testen, ob der zum Zuordnen der Zuordnung verwendete Fremdschlüssel vorhanden ist, und nicht das zugeordnete Objekt selbst. http://guides.rubyonrails.org/active_record_validations_callbacks.html

attr_accessible :project_id 
+3

Das ist ein direktes Zitat aus der Dokumentation, aber in meinen Tests mit Rails 3.2.3 funktioniert es genau umgekehrt. Wenn Sie die Anwesenheit in der Assoziation überprüfen (': project,: presentation => true'), schlägt die Validierung fehl, wenn die project_id nicht in der Projekttabelle vorhanden ist. Aber wenn Sie die Präsenz auf der ID validieren (': project_id,: presentation => true'), gibt '.valid?' 'True' zurück, egal welchen Wert Sie der project_id zuweisen. Das Hinzufügen von 'validates: project,: associated => true' hilft nicht - es akzeptiert immer noch nicht vorhandene project_ids. Ziemlich verwirrt darüber, wie man Assoziationen validieren soll! –

3

EDIT: unter der Annahme, dass der Verein nicht optional ...

Die einzige Art, wie ich es gründlich bekommen zu validieren, ist dies:

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} 

Die erste Zeile überprüft, ob das zugehörige Projekt gültig ist, wenn es einen gibt. Die zweite Zeile besteht darauf, dass project_id vorhanden ist, es sei denn, das zugehörige Projekt existiert und ist neu (wenn es sich um einen neuen Datensatz handelt, hat es noch keine ID). Die dritte Zeile stellt sicher, dass das Projekt vorhanden ist, wenn eine ID vorhanden ist, d. H. Wenn das zugeordnete Projekt bereits gespeichert wurde.

ActiveRecord weist der Aufgabe eine project_id zu, wenn Sie ein gespeichertes Projekt project zuweisen. Wenn Sie ein nicht gespeichertes/neues Projekt project zuweisen, wird project_id leer gelassen. Daher möchten wir sicherstellen, dass project_id vorhanden ist, aber nur wenn es sich um ein gespeichertes Projekt handelt; Dies wird in Zeile 2 oben erreicht. Wenn Sie also project_id eine Zahl zuweisen, die ein reales Projekt darstellt, füllt ActiveRecord project mit dem entsprechenden Projektobjekt aus. Aber wenn die ID, die Sie zugewiesen haben, gefälscht ist, wird project als Null verlassen. So Zeile drei oben, die gewährleistet, dass wir eine project haben, wenn project_id ausgefüllt ist - wenn Sie eine falsche ID liefern, wird dies fehlschlagen.

Siehe RSpec Beispiele, die diese Validierungen testen: https://gist.github.com/kianw/5085085

2

Joshua Muheim-Lösung funktioniert, aber ich hasse es nicht in der Lage ist, sein, einfach ein Projekt zu einer Aufgabe mit einer ID wie dieser Link:

t = Task.new 
t.project_id = 123 # Won't verify if it's valid or not. 

So kam ich stattdessen mit dieser:

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 
Verwandte Themen