Rails 3.1, RSpec: la sperimentazione del modello di convalide

Ho iniziato il mio viaggio con il TDD in Rails e hanno incontrato un piccolo problema per quanto riguarda i test per il modello validazioni che non riesco a trovare una soluzione. Diciamo che ho un Utente modello,

class User < ActiveRecord::Base
  validates :username, :presence => true
end

e un semplice test

it "should require a username" do
  User.new(:username => "").should_not be_valid
end

Correttamente questo test la presenza di convalida, ma se voglio essere più specifico? Per esempio, il test di full_messages sugli errori oggetto..

it "should require a username" do
  user = User.create(:username => "")
  user.errors[:username].should ~= /can't be blank/
end

La mia preoccupazione per il tentativo iniziale (utilizzando should_not be_valid) è che RSpec non produrrà un messaggio di errore descrittivo. Dice semplicemente “previsto valido? per restituire false, avuto il vero.” Tuttavia, la seconda prova esempio, ha un piccolo inconveniente: si utilizza il metodo di creazione invece che il nuovo metodo per ottenere l’oggetto di errore.

Vorrei che il mio test per essere più specifico su quello che stai testando, ma allo stesso tempo non dovrà toccare un database.

Qualcuno ha qualche input?

InformationsquelleAutor Feech | 2011-09-24

 

7 Replies
  1. 96

    Prima di tutto vorrei dire che avete un malvagio nome.

    Secondo, COMPLIMENTI per questo si impegna in TDD con ROR prometto una volta che andare avanti, non guardare indietro.

    Più semplice e veloce soluzione sporca sarà quello di generare un nuovo modello valido prima di ogni test come questo:

     before(:each) do
        @user = User.new
        @user.username = "a valid username"
     end

    MA quello che suggerisco è che si istituito fabbriche per tutti i modelli che genererà un valido modello per voi automaticamente e quindi si possono confondere con i singoli attributi, e vedere se la convalida. Mi piace usare FactoryGirl per questo:

    Fondamentalmente, una volta che si ottiene impostare il test sarebbe simile a questa:

    it "should have valid factory" do
        FactoryGirl.build(:user).should be_valid
    end
    
    it "should require a username" do
        FactoryGirl.build(:user, :username => "").should_not be_valid
    end

    Oh ya e qui è un buon railscast che spiega tutto meglio di me:

    buona fortuna 🙂


    AGGIORNAMENTO: Come di versione 3.0 la sintassi per la factory girl è cambiato. Ho modificato il mio codice di esempio per riflettere questo.

    • Grazie mille Matteo. C’è un modo per ottenere più vicino a l’errore sto cercando di test? X. should_not be_valid sembra così generica per me, e chissà se qualcosa giù per la strada farà il record non valido. Questo test avrà esito positivo nel posto sbagliato. A proposito, penso che ho segnato la tua risposta accettata. Non ho?
    • A destra, quindi questo è il motivo per cui sostengo per le fabbriche. È possibile scrivere il codice per produrre un utente valido una sola volta in un posto e poi si scrive un test per assicurarsi che il suo valido prima di tutte le prove individuali che assicurarsi che si può invalidare esso. In questo modo, se per qualche motivo non potete modificare il modello in modo che la fabbrica produce più di un utente valido il Factory.build(:user).should be_valid test avrà esito negativo e si sa è necessario aggiornare la vostra fabbrica… capito? (e sì, è accettato my7 risposta)
    • FactoryGirl.costruire(:utente, nome utente: “).dovrebbe avere(1).errors_on (username)
    • Per me la chiave è stato utilizzando build (o se non si utilizza FactoryGirl, new) piuttosto che create. In caso contrario, un ActiveRecord::RecordInvalid eccezione viene sollevata prima della prova completa, causando un errore.
    • Non prova questo modo! Vedere nathanvda la risposta qui sotto. Se si prova in questo modo, si sta essenzialmente test ActiveRecord del comportamento, che è già collaudato. Invece, utilizzare il shoulda-matcher la gemma solo di verificare che l’Utente abbia la convalida in luogo.
    • Perché questo sopra spec lavoro durante l’utilizzo di build? Ho pensato che è necessario utilizzare create per ottenere AR convalide per l’esecuzione?

  2. 43

    Un modo più semplice per testare il modello, le convalide e molto di più di active record) è quello di utilizzare una gemma come shoulda o notevole.

    Che permetteranno alla prova come segue:

    describe User
    
      it { should validate_presence_of :name }
    
    end
    • Questo è bene verificare che siano presenti le associazioni di modelli, ma essere consapevoli del fatto che in realtà non prova a creare un utente senza nome e verificare la sua validità
    • no in realtà, per quanto ne so questo è esattamente ciò che shoulda fa: si cercherà di creare l’oggetto con un nome vuoto, e dovrebbe dare un errore.
    • Hai ragione, mi sembra di leggere il codice sbagliato github.com/thoughtbot/shoulda-matchers/blob/master/lib/shoulda/…
  3. 16

    Provare questo:

    it "should require a username" do
      user = User.create(:username => "")
      user.valid?
      user.errors.should have_key(:username)
    end
    • Questo è il mio preferito, molto solido, controlla che la chiave e non il messaggio, che è un dettaglio
    • si può semplicemente utilizzare user = Utente.nuovo(:username => “”) per evitare di colpire db
    • non sarà colpito db livello di convalide, per esempio una unicità del vincolo di indice.
    • La domanda, in particolare chiedere di non dover toccare db
    • Buona pesca, che ho perso. È bene notare però per chi è alla ricerca di una più completa del test di validazione
  4. 4

    nella nuova versione rspec, si dovrebbe utilizzare aspettare che invece dovrebbe, altrimenti si otterrà attenzione:

    it "should have valid factory" do
        expect(FactoryGirl.build(:user)).to be_valid
    end
    
    it "should require a username" do
        expect(FactoryGirl.build(:user, :username => "")).not_to be_valid
    end
    • Si dovrebbe anche usare il presente indicativo dei verbi invece dovrebbero nomi di esempio. Il sopra può essere riscritta come "has a valid factory" e "requires a username".
  5. 0

    Ho tradizionalmente gestiti contenuto di errore specifiche in funzione o richiesta di specifiche. Così, per esempio, ho una simile spec che cercherò di condensare sotto:

    Funzione Spec Esempio

    before(:each) { visit_order_path }
    
    scenario 'with invalid (empty) description' , :js => :true do
    
      add_empty_task                                 #this line is defined in my spec_helper
    
      expect(page).to have_content("can't be blank")

    Allora, ho il mio modello spec test se è qualcosa di valido, ma poi la mia funzione di spec che verifica l’output esatto del messaggio di errore. Cordiali saluti, queste funzionalità specifiche richiedono Capibara, che può essere trovato qui.

  6. 0

    Come @nathanvda detto, vorrei approfittare di Thoughtbot del Shoulda Matcher gemma. Con quella a dondolo, è possibile scrivere i test nel seguente modo, come per il test di presenza, così come qualsiasi messaggio di errore personalizzato.

    RSpec.describe User do
    
      describe 'User validations' do
        let(:message) { "I pitty da foo who dont enter a name" }
    
        it 'validates presence and message' do
         is_expected.to validate_presence_of(:name).
          with_message message
        end
    
        # shorthand syntax:
        it { is_expected.to validate_presence_of(:name).with_message message }
      end
    
    end
  7. 0

    Un po ‘ in ritardo alla festa, ma se non si desidera aggiungere shoulda matcher, questo dovrebbe funzionare con rspec-rails e factorybot:

    # ./spec/factories/user.rb
    FactoryBot.define do
      factory :user do
        sequence(:username) { |n| "user_#{n}" }
      end
    end
    
    # ./spec/models/user_spec.rb
    describe User, type: :model do
      context 'without a username' do
        let(:user) { create :user, username: nil }
    
        it "should NOT be valid with a username error" do
          expect(user).not_to be_valid
          expect(user.errors).to have_key(:username)
        end
      end
    end

Lascia un commento