2012-05-18 9 views
16

Mentre si passa attraverso il tutorial di Ruby On Rails di Michael Hartl, nella sezione in cui l'autore scrive il test di integrazione per convalidare la sua pagina di iscrizione, ha usato il codice spinet qui sotto. Ho ottenuto ciò che fa il codice ma non riuscivo a capire come parte non riuscisse a capire l'ordine di esecuzione.Come funziona RSpec in ROR

expect { click_button "Create my account" }.not_to change(User, :count) 

Qualcuno può spiegare la semantica della suddetta catena di metodi e blocchi e come si adattano insieme?

+3

Questo non è Capybara, è RSpec. –

+0

@RyanBigg Grazie, l'ho corretto. – Bedasso

risposta

45

usereste expect ... change per verificare che un particolare metodo di chiamata cambia - o non cambia - qualche altro valore. In questo caso:

expect { click_button "Create my account" }.not_to change(User, :count) 

causerà RSpec per effettuare le seguenti operazioni:

  1. Run User.count e prendere nota del valore restituito. (Questo può essere specificato come un ricevitore e un nome di metodo, come (User, :count) nel tuo esempio, o come un blocco arbitrario di codice, come { User.count }.
  2. Run click_button "Create my account" che è un metodo Capybara che simula un clic del mouse su un link.
  3. ..
  4. Run User.count nuovo
  5. confrontare i risultati del # 1 e # 3 Se si differenziano, l'esempio non riesce Se sono uguali, passa

altri modi per utilizzare expect ... change:..

expect { thing.destroy }.to change(Thing, :count).from(1).to(0) 
expect { thing.tax = 5 }.to change { thing.total_price }.by(5) 
expect { thing.save! }.to raise_error 
expect { thing.symbolize_name }.to change { thing.name }.from(String).to(Symbol) 

Alcuni documenti sono here.

Come questo è un po 'arcano, e non è affatto necessario capire come funziona per usarlo. Una chiamata a expect sta definendo una struttura per rspec da eseguire, utilizzando la DSL personalizzata di rspec e il sistema di "matcher". Gary Bernhardt ha un piuttosto elegante screencast in cui sostiene che il mistero di rspec in realtà cade naturalmente da un linguaggio dinamico come il rubino.Non è una buona introduzione a utilizzando rspec, ma se sei curioso di sapere come funziona, potresti trovarlo interessante.

UPDATE

Dopo aver visto il tuo commento su un'altra risposta, aggiungerò un po 'l'ordine delle operazioni. Il trucco non intuitivo è che è il match (change in questo caso) che esegue tutti i blocchi. expect ha un lambda, not_to è un alias per should_not il cui compito è quello di passare il lambda al matcher. Il matcher in questo caso è change che sa eseguire il proprio argomento una volta, quindi esegue il lambda che è stato passato (quello da expect), quindi esegue nuovamente il proprio argomento per vedere se le cose cambiano. È complicato perché sembra che la linea debba essere eseguita da sinistra a destra, ma dal momento che la maggior parte dei pezzi passa semplicemente attorno a blocchi di codice, possono e li possono mescolare in qualsiasi ordine sia più sensato per il matcher.

Non sono un esperto degli interni di rspec, ma questa è la mia comprensione dell'idea di base.

+0

La risposta più dettagliata e utile che abbia mai letto su SO. Potrei revocare questo 100x se potessi. Grazie! –

4

Ecco un estratto dal Ryan Bates Railscast on Request Specs and Capybara

require 'spec_helper' 

describe "Tasks" do 
    describe "GET /tasks" do 
    it "displays tasks" do 
     Task.create!(:name => "paint fence") 
     visit tasks_path 
     page.should have_content("paint fence") 
    end 
    end 

    describe "POST /tasks" do 
    it "creates a task" do 
     visit tasks_path 
     fill_in "Name", :with => "mow lawn" 
     click_button "Add" 
     page.should have_content("Successfully added task.") 
     page.should have_content("mow lawn") 
    end 
    end 
end 

Ed ecco un estratto dal the docs on RSPec Expectations

describe Counter, "#increment" do 
    it "should increment the count" do 
    expect{Counter.increment}.to change{Counter.count}.from(0).to(1) 
    end 

    # deliberate failure 
    it "should increment the count by 2" do 
    expect{Counter.increment}.to change{Counter.count}.by(2) 
    end 
end 

Quindi, in pratica, il

expect { click_button "Create my account" }.not_to change(User, :count) 

fa parte RSpec:

expect {...}.not_to change(User, :count) 

e parte Capybara

click_button "Create my account" 

(Ecco a link to the Capyabara DSL - è possibile cercare click_button)

Sembra che tu stia cercando un esempio generale con entrambi. Questo non è un esempio perfetto, ma potrebbe essere simile a questa:

describe "Tasks" do 
    describe "GET /tasks" do 
    it "displays tasks" do 
     expect { click_button "Create my account" }.not_to change(User, :count) 
    end 
    end 
end 
+0

Tnx per i bei esempi. Ero più interessato a conoscere la semantica dell'espressione, come il modo in cui la dose 'cambia (..)' viene eseguita due volte e l'ordine generale di esecuzione. – Bedasso