2012-07-04 11 views
17

Sono ancora abbastanza nuovo per i test in Rails 3 e utilizzo RSpec e Remarkable. Ho letto un sacco di post e alcuni libri già, ma sono ancora abbastanza incerto quando usare il nome dell'associazione, quando è il suo ID.Il modo perfetto per convalidare e testare le associazioni di Rails 3 (usando RSpec/Remarkable)?

class Project < ActiveRecord::Base 
    has_many :tasks 
end 

class Task < ActiveRecord::Base 
    belongs_to :project 
end 

A causa di buona pratica, voglio proteggere i miei attributi da assegnazioni di massa:

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

    belongs_to :project 
end 

Prima di tutto, voglio fare in modo che un progetto mai esiste senza un compito valida:

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

voglio anche fare in modo che il progetto assegnato o l'ID del progetto è sempre valido:

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

... e ho bisogno della validazione su: presenza quando uso: associata ??

Grazie mille per chiarire, sembra che dopo ore di lettura e cercando di testare roba utilizzando RSpec/Shoulda/Notevole non vedo la foresta a causa di Anymore tutti gli alberi ...

+0

Bella domanda chiara. Per confermare, non intendi che vuoi "assicurarti che un'attività ** non ** non esista senza un progetto (genitore) valido"? –

risposta

12

Questo sembra essere il modo giusto per farlo:

attr_accessible :project_id 

Non devi mettere :project anche lì! E 'comunque possibile fare task.project=(Project.first!)

Poi verificare l'esistenza del :project_id utilizzando la seguente (:project_id è impostato anche quando si utilizza task.project=(...)):

validates :project_id, :presence => true 

Ora assicurarsi che un progetto associato è valida come questo :

validates :project, :associated => true 

Quindi:

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! 

È un po 'un peccato che quando si assegna un ID attraverso t.project_id = non è verificato se questo ID specifico esiste davvero. È necessario controllare questo utilizzando una convalida personalizzata o utilizzando lo Validates Existence GEM.

per testare queste associazioni utilizzando RSpec con matchers notevoli, fare qualcosa di simile:

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

Wow, questo è un rivelatore. E sto ottenendo risultati inaspettati con Rails 3.2.3. Prova questo. Nel modello Task, aggiungi 'validates: project,: presence => true' ma nessuna convalida': associated'. Nella console, crea una nuova attività con project_id = 999 e controlla se è '.valid?'. Rails cerca il record nella tabella dei progetti, quindi (presumibilmente perché non è stato trovato e project_id è ancora nullo), la validazione fallisce. Ora cambia la validazione per 'validates: project_id,: presence => true'. Rails NON legge i progetti e la convalida ha esito positivo. Aggiungi ': associated' e la validazione * still * ha esito positivo. –

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

Se si vuole essere sicuri che sia presente un'associazione, è necessario il test se la chiave esterna utilizzata per mappare l'associazione è presente, e non l'oggetto associato stesso. http://guides.rubyonrails.org/active_record_validations_callbacks.html

attr_accessible :project_id 
+3

Questa è una citazione diretta dalla documentazione, ma nei miei test con Rails 3.2.3, funziona esattamente al contrario. Se convalidate la presenza sull'associazione (': project,: presence => true'), fallisce la convalida se project_id non esiste nella tabella dei progetti. Ma se convalidate la presenza sull'id (': project_id,: presence => true'),' .valid? 'Restituisce' true' indipendentemente dal valore assegnato a project_id. L'aggiunta di 'validates: project,: associated => true' non aiuta - accetta ancora project_ids inesistenti. Abbastanza confuso su come si dovrebbe convalidare le associazioni! –

3

EDIT: supponendo che l'associazione non è un optional ...

L'unico modo in cui posso farlo per convalidare fondo è questo:

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} 

La prima linea di convalida se il progetto associato è valido, se ce n'è uno. La seconda riga insiste sul fatto che project_id sia presente, a meno che il Progetto associato esista e sia nuovo (se si tratta di un nuovo record, non avrà ancora un ID). La terza riga garantisce che il Progetto sia presente se è presente un ID, cioè se il Progetto associato è già stato salvato.

ActiveRecord assegnerà un project_id all'attività se si assegna un progetto salvato a project. Se si assegna un nuovo progetto non salvato a project, lascerà vuoto lo project_id. Pertanto, vogliamo garantire che sia presente project_id, ma solo quando si ha a che fare con un Progetto salvato; questo è compiuto nella riga due sopra.

Al contrario, se si assegna un numero a project_id che rappresenta un progetto reale, ActiveRecord inserirà project con l'oggetto Progetto corrispondente. Ma se l'ID che hai assegnato è fasullo, lascerà lo project come zero. Quindi la riga tre sopra, che assicura che abbiamo uno se project_id è compilato - se fornisci un ID falso, questo fallirà.

esempi

See RSpec che mettono alla prova queste convalide: https://gist.github.com/kianw/5085085

2

soluzione di Joshua Muheim funziona, ma io odio essere non essere in grado di collegare semplicemente un progetto per un'attività con un id in questo modo:

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

Quindi ho pensato:

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 
Problemi correlati