RSpec: qual è la differenza tra let e a before block?


90

Qual è la differenza tra lete abefore blocco in RSpec?

E quando usarli?

Quale sarà un buon approccio (let o prima) nell'esempio seguente?

let(:user) { User.make !}
let(:account) {user.account.make!}

before(:each) do
 @user = User.make!
 @account = @user.account.make!
end

L'ho studiato post di stackoverflow

Ma è bene definire let per cose di associazione come sopra?


1
Fondamentalmente, "let" viene utilizzato da persone che non amano le variabili di istanza. Come nota a margine, dovresti considerare l'utilizzo di FactoryGirl o uno strumento simile.
apneadiving

Risposte:


146

Le persone sembrano aver spiegato alcuni dei modi fondamentali in cui differiscono, ma tralasciate before(:all) e non spiegano esattamente perché dovrebbero essere utilizzate.

È mia convinzione che le variabili di istanza non abbiano spazio per essere utilizzate nella stragrande maggioranza delle specifiche, in parte a causa dei motivi menzionati in questa risposta , quindi non le menzionerò come opzione qui.

lascia i blocchi

Codice all'interno di un file let blocco viene eseguito solo se referenziato, il caricamento lento significa che l'ordinamento di questi blocchi è irrilevante. Questo ti dà una grande quantità di potenza per ridurre la configurazione ripetuta attraverso le tue specifiche.

Un esempio (estremamente piccolo e artificioso) di questo è:

let(:person)     { build(:person) }
subject(:result) { Library.calculate_awesome(person, has_moustache) }

context 'with a moustache' do
  let(:has_moustache) { true } 
  its(:awesome?)      { should be_true }
end

context 'without a moustache' do
  let(:has_moustache) { false } 
  its(:awesome?)      { should be_false }
end

Puoi vedere che has_moustacheè definito in modo diverso in ogni caso, ma non è necessario ripetere la subjectdefinizione. Qualcosa di importante da notare è che l'ultimolet blocco definito nel contesto corrente. Questo è utile per impostare un valore predefinito da utilizzare per la maggior parte delle specifiche, che può essere sovrascritto se necessario.

Ad esempio, controllando il valore restituito di calculate_awesomese passato un personmodello con top_hatimpostato su true, ma senza baffi sarebbe:

context 'without a moustache but with a top hat' do
  let(:has_moustache) { false } 
  let(:person)        { build(:person, top_hat: true) }
  its(:awesome?)      { should be_true }
end

Un'altra cosa da notare sui blocchi let, non dovrebbero essere usati se stai cercando qualcosa che è stato salvato nel database (cioè Library.find_awesome_people(search_criteria)) poiché non saranno salvati nel database a meno che non siano già stati referenziati. let!o beforeblocchi sono ciò che dovrebbe essere usato qui.

Inoltre, non usare mai e poi maibefore per attivare l'esecuzione di letblocchi, ecco cosalet! è fatto!

permettere! blocchi

let!i blocchi vengono eseguiti nell'ordine in cui sono definiti (molto simile a un blocco precedente). L'unica differenza fondamentale rispetto ai blocchi precedenti è che ottieni un riferimento esplicito a questa variabile, invece di dover ricorrere alle variabili di istanza.

Come per i letblocchi, se let!vengono definiti più blocchi con lo stesso nome, il più recente è quello che verrà utilizzato in esecuzione. La differenza principale è che i let!blocchi verranno eseguiti più volte se usati in questo modo, mentre il letblocco verrà eseguito solo l'ultima volta.

prima di (: each) blocchi

before(:each)è l'impostazione predefinita prima del blocco e pertanto è possibile fare riferimento come before {}anziché specificare before(:each) {}ogni volta l'intero .

È mia preferenza personale utilizzare i beforeblocchi in alcune situazioni fondamentali. Userò prima dei blocchi se:

  • Sto usando la derisione, lo stubbing o il doppio
  • Esiste una configurazione di dimensioni ragionevoli (generalmente questo è un segno che i tratti di fabbrica non sono stati impostati correttamente)
  • C'è un numero di variabili a cui non ho bisogno di fare riferimento direttamente, ma sono necessarie per l'installazione
  • Sto scrivendo test di controller funzionali in rails, e voglio eseguire una richiesta specifica per testare (cioè before { get :index }). Anche se potresti usarlo subjectin molti casi, a volte sembra più esplicito se non hai bisogno di un riferimento.

Se ti ritrovi a scrivere beforeblocchi di grandi dimensioni per le tue specifiche, controlla le tue fabbriche e assicurati di comprendere appieno i tratti e la loro flessibilità.

prima (: tutti) blocchi

Questi vengono eseguiti solo una volta, prima delle specifiche nel contesto corrente (e dei suoi figli). Questi possono essere usati con grande vantaggio se scritti correttamente, poiché in alcune situazioni ciò può ridurre l'esecuzione e lo sforzo.

Un esempio (che difficilmente influenzerebbe affatto il tempo di esecuzione) è il mock-out di una variabile ENV per un test, che dovresti fare solo una volta.

Spero che aiuti :)


Per gli utenti minitest, che sono ma non ho notato il tag RSpec di questa domanda e risposta, l' before(:all)opzione non esiste in Minitest. Ecco alcune soluzioni alternative nei commenti: github.com/seattlerb/minitest/issues/61
MrYoshiji

L'articolo a cui si fa riferimento è un link morto: \
Dylan Pierce

Grazie @DylanPierce. Non riesco a trovare alcuna copia di quell'articolo, quindi ho fatto riferimento a una risposta SO che affronta invece questo :)
Jay

Grazie :) molto apprezzato
Dylan Pierce

1
itsnon è più in rspec-core. Un modo più moderno e idiomatico è it { is_expected.to be_awesome }.
Zubin

26

Quasi sempre, preferisco let. Il post che colleghi specifica che letè anche più veloce. Tuttavia, a volte, quando devono essere eseguiti molti comandi, potrei usare before(:each)perché la sua sintassi è più chiara quando sono coinvolti molti comandi.

Nel tuo esempio, preferirei sicuramente usare letinvece di before(:each). In generale, quando viene eseguita solo l'inizializzazione di alcune variabili, tendo a preferire l'uso let.


15

Una grande differenza che non è stata menzionata è che le variabili definite con letnon vengono istanziate fino a quando non le chiami la prima volta. Quindi, mentre un before(:each)blocco istanzia tutte le variabili, letdefiniamo un numero di variabili che potresti utilizzare in più test, non le istanzia automaticamente. Senza saperlo, i tuoi test potrebbero tornare a morderti se ti aspetti che tutti i dati vengano caricati in anticipo. In alcuni casi, potresti persino voler definire un numero di letvariabili, quindi utilizzare un before(:each)blocco per chiamare ogni letistanza solo per assicurarti che i dati siano disponibili per cominciare.


20
È possibile utilizzare let!per definire metodi che vengono chiamati prima di ogni esempio. Vedere la documentazione di RSpec .
Jim Stewart

3

Sembra che tu stia usando Machinist. Attenzione, potresti riscontrare alcuni problemi con make! all'interno di let (la versione non bang) che si verifica al di fuori della transazione globale dei dispositivi (se si utilizzano anche dispositivi transazionali), corrompendo così i dati per gli altri test.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.