2015-07-24 24 views
16

Ho un metodo che utilizza DateTime.now per eseguire una ricerca su alcuni dati, voglio testare il metodo con varie date ma non so come smistare DateTime. ora né posso farlo funzionare con Timecop (se funziona anche così).Rails - Test di un metodo che utilizza DateTime.now

Con il tempo ho cercato poliziotto

it 'has the correct amount if falls in the previous month' do 
     t = "25 May".to_datetime 
     Timecop.travel(t) 
     puts DateTime.now 

     expect(@employee.monthly_sales).to eq 150 
end 

quando corro le specifiche posso vedere che mette DateTime.now dà 2015-05-25T01:00:00+01:00 ma avendo le stesse mette DateTime.Now all'interno del metodo che sto testando uscite 2015-07-24T08:57:53+01:00 (oggi Data). Come posso realizzare questo?

------------------ aggiornamento --------------------------- ------------------------

Stavo configurando i record (@employee, ecc.) In un blocco before(:all) che sembra aver causato il problema. Funziona solo quando la configurazione viene eseguita dopo il blocco Timecop do. Perché è così?

+0

Il tuo codice dovrebbe funzionare (ricordati di chiamare 'Timecop.return' quando hai finito). Puoi incollare il corpo del metodo 'monthly_sales'? – tompave

+0

il metodo è irrilevante diverso da quello che chiama DateTime.now –

+0

Quindi temo di non poterti aiutare. – tompave

risposta

8

TL; DR: Il problema era che DateTime.now è stato chiamato in Employee prima che Timecop.freeze fosse chiamato nelle specifiche.

Timecop deride il costruttore di Time, Date e DateTime. Qualsiasi istanza creata tra freeze e return (o all'interno di un blocco freeze) verrà derisa.
Qualsiasi istanza creata prima di freeze o successiva a return non sarà interessata poiché Timecop non interferisce con gli oggetti esistenti.

Dal README (il corsivo è mio):

Un gioiello che fornisce "viaggio nel tempo" e "tempo di congelamento" capacità, il che rende morto semplice per testare il codice dipendente dal tempo. Fornisce un metodo unificato per simulare Time.now, Date.today e DateTime.now in una singola chiamata.

quindi è essenziale per chiamare Timecop.freeze prima di creare l'oggetto Time si vuole prendere in giro. Se si freeze in un blocco RSpec before, verrà eseguito prima che venga valutato subject. Tuttavia, se disponi di un blocco before in cui hai impostato l'oggetto (@employee nel tuo caso) e disponi di un altro bloccoin uno nidificato n. describe, l'oggetto è già impostato, dopo aver chiamato DateTime.new prima di bloccare il tempo.


Che cosa succede se si aggiunge quanto segue al Employee

class Employee 
    def now 
    DateTime.now 
    end 
end 

quindi si esegue le seguenti specifiche:

describe '#now' do 
    let(:employee) { @employee } 
    it 'has the correct amount if falls in the previous month', focus: true do 
    t = "25 May".to_datetime 
    Timecop.freeze(t) do 
     expect(DateTime.now).to eq t 
     expect(employee.now).to eq t 

     expect(employee.now.class).to be DateTime 
     expect(employee.now.class.object_id).to be DateTime.object_id 
    end 
    end 
end 

Invece di utilizzare un blocco freeze, è anche possibile freeze e return in rspec before e after ganci:

describe Employee do 
    let(:frozen_time) { "25 May".to_datetime } 
    before { Timecop.freeze(frozen_time) } 
    after { Timecop.return } 
    subject { FactoryGirl.create :employee } 

    it 'has the correct amount if falls in the previous month' do 
    # spec here 
    end 

end 

off-topic, ma forse uno sguardo ai http://betterspecs.org/

+0

questa specifica passa –

+0

Se 'expect (employee.now) .to eq t' passa allora Timecop funziona correttamente. Hai provato le tue specifiche all'interno di un blocco 'Timecop.freeze'? – amiuhle

+0

funziona all'interno di un blocco di blocco ma solo quando i dati sono impostati all'interno di esso –

3

Timecop dovrebbe essere in grado di gestire ciò che si desidera. Cerca di bloccare il tempo prima di eseguire il test invece di solo viaggiare, quindi sbloccare quando hai finito. Come questo:

before do 
    t = "25 May".to_datetime 
    Timecop.freeze(t) 
end 

after do 
    Timecop.return 
end 

it 'has the correct amount if falls in the previous month' do 
    puts DateTime.now 
    expect(@employee.monthly_sales).to eq 150 
end 

Da readme di Timecop:

congelamento è usato per deridere in modo statico il concetto di ora. Mentre il programma viene eseguito, Time.now non cambierà se non si effettuano chiamate successive nell'API Timecop. travel, d'altra parte, calcola un offset tra ciò che pensiamo attualmente Time.now è (ricordiamo che supportiamo i viaggi nidificati) e il tempo passato. Utilizza questo offset per simulare il passare del tempo.

Quindi vuoi congelare il tempo in un determinato luogo, piuttosto che viaggiare solo in quel momento. Poiché il tempo passerà con un viaggio normalmente, ma da un diverso punto di partenza.

Se questo ancora non funziona, si può mettere il chiamata di metodo in un blocco con Timecop per garantire che essa è il congelamento del tempo all'interno del blocco come:

t = "25 May".to_datetime 
Timecop.travel(t) do # Or use freeze here, depending on what you need 
    puts DateTime.now 
    expect(@employee.monthly_sales).to eq 150 
end 
+0

entrambi falliscono ancora –

0

mi sono imbattuto in diversi problemi con Timecop e altre cose magia che pasticci con data, ora e DateTime classi e le loro metodi. Ho scoperto che è meglio utilizzare solo l'iniezione di dipendenza, invece:

codice dipendenti

class Employee 
    def monthly_sales(for_date = nil) 
    for_date ||= DateTime.now 

    # now calculate sales for 'for_date', instead of current month 
    end 
end 

Spec

it 'has the correct amount if falls in the previous month' do 
    t = "25 May".to_datetime 
    expect(@employee.monthly_sales(t)).to eq 150 
end 

Noi, popoli del mondo Ruby, troviamo un grande piacere usando alcuni trucchi magici, che le persone che usano linguaggi di programmazione meno espressivi non sono in grado di utilizzare. Ma questo è il caso in cui la magia è troppo oscura e dovrebbe essere davvero evitata. Basta usare invece l'approccio di best practice generalmente accettato per le dipendenze.