ruby - run - rspec spectroscopy




Quando usare RSpec let()? (6)

"before" di default implica before(:each) . Ref The Rspec Book, copyright 2010, pagina 228.

before(scope = :each, options={}, &block)

Io uso before(:each) per seminare alcuni dati per ogni gruppo di esempio senza dover chiamare il metodo let per creare i dati nel blocco "it". Meno codice nel blocco "it" in questo caso.

Io uso let se voglio alcuni dati in alcuni esempi ma non in altri.

Sia prima sia let sono grandi per l'ESSICAZIONE dei blocchi "it".

Per evitare qualsiasi confusione, "let" non è la stessa di before(:all) . "Let" rivaluta il suo metodo e valore per ogni esempio ("it"), ma memorizza il valore in più chiamate nello stesso esempio. Puoi leggere maggiori informazioni qui: relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/…

Tendo ad usare prima dei blocchi per impostare le variabili di istanza. Quindi uso queste variabili nei miei esempi. Di recente mi sono imbattuto in let() . Secondo i documenti di RSpec, è usato per

... per definire un metodo helper memoized. Il valore verrà memorizzato nella cache su più chiamate nello stesso esempio ma non tra gli esempi.

In che modo è diverso dall'usare le variabili di istanza prima dei blocchi? E anche quando dovresti usare let() vs before() ?


È importante tenere presente che let è valutato pigro e non vi inserisce metodi di effetti collaterali altrimenti non sarebbe possibile modificare facilmente da let a before (: each) . Puoi usare let! invece di lasciare che sia valutato prima di ogni scenario.


In generale, let() è una sintassi più @name e ti consente di @name simboli @name . Ma, caveat emptor! Ho scoperto che let() introduce anche dei bug sottili (o almeno dei grattacapi) perché la variabile non esiste realmente fino a quando non provi ad usarla ... Dì a racconto: se aggiungi un puts dopo il let() per vederlo la variabile è corretta consente a una specifica di passare, ma senza le puts la specifica fallisce - hai trovato questa sottigliezza.

Ho anche scoperto che let() non sembra memorizzare nella cache in tutte le circostanze! L'ho scritto nel mio blog: http://technicaldebt.com/?p=1242

Forse sono solo io?


Io uso let per testare le mie risposte HTTP 404 nelle mie specifiche API utilizzando i contesti.

Per creare la risorsa, io uso let! . Ma per memorizzare l'identificatore di risorse, io uso let . Guarda come appare:

let!(:country)   { create(:country) }
let(:country_id) { country.id }
before           { get "api/countries/#{country_id}" }

it 'responds with HTTP 200' { should respond_with(200) }

context 'when the country does not exist' do
  let(:country_id) { -1 }
  it 'responds with HTTP 404' { should respond_with(404) }
end

Ciò mantiene le specifiche pulite e leggibili.


Let è funzionale come essenzialmente un Proc. Anche la sua cache.

Una cosa che ho trovato subito con let ... In un blocco Spec che sta valutando un cambiamento.

let(:object) {FactoryGirl.create :object}

expect {
  post :destroy, id: review.id
}.to change(Object, :count).by(-1)

Dovrai essere sicuro di chiamare let al di fuori del tuo blocco previsto. cioè stai chiamando FactoryGirl.create nel tuo let block. Solitamente lo faccio verificando che l'oggetto sia persistente.

object.persisted?.should eq true

Altrimenti, quando il blocco let viene chiamato la prima volta, un cambiamento nel database si verificherà effettivamente a causa della lazy istanza.

Aggiornare

Aggiungendo solo una nota. Fai attenzione a giocare a golf con il codice o, in questo caso, a golf con questa risposta.

In questo caso, devo solo chiamare un metodo a cui l'oggetto risponde. Quindi invoco il _.persisted? _ metodo sull'oggetto come verità. Tutto quello che sto cercando di fare è istanziare l'oggetto. Potresti chiamare vuoto? o nulla? pure. Il punto non è il test ma portare l'oggetto della vita chiamandolo.

Quindi non puoi refactoring

object.persisted?.should eq true

essere

object.should be_persisted 

come l'oggetto non è stato istanziato ... è pigro. :)

Aggiornamento 2

sfruttare il let! sintassi per la creazione immediata di oggetti, che dovrebbe evitare del tutto questo problema. Nota però sconfiggerà molto lo scopo della pigrizia del let non sbattuto.

Inoltre, in alcuni casi potresti effettivamente sfruttare la sintassi dell'oggetto anziché let in quanto potrebbe darti ulteriori opzioni.

subject(:object) {FactoryGirl.create :object}

Nota per Joseph: se stai creando oggetti di database in un before(:all) questi non verranno catturati in una transazione e molto più probabilmente lascerai cruft nel tuo database di test. Utilizzare before(:each) invece.

L'altra ragione per usare let e la sua valutazione lazy è così che puoi prendere un oggetto complicato e testare pezzi individuali facendo overriding in contesti, come in questo esempio molto forzato:

context "foo" do
  let(:params) do
     { :foo => foo,  :bar => "bar" }
  end
  let(:foo) { "foo" }
  it "is set to foo" do
    params[:foo].should eq("foo")
  end
  context "when foo is bar" do
    let(:foo) { "bar" }
    # NOTE we didn't have to redefine params entirely!
    it "is set to bar" do
      params[:foo].should eq("bar")
    end
  end
end




rspec