2009-04-03 6 views
5

Qual è il modo migliore per scrivere il test dell'unità per il codice che ottiene l'ora corrente? Ad esempio alcuni oggetti potrebbero essere creati solo in giorni lavorativi, altri oggetti tengono conto dell'ora corrente quando controllano le autorizzazioni per eseguire alcune azioni, ecc.Codice di verifica dell'unità che ottiene l'ora corrente

Immagino che dovrei prendere in giro Data e oggi e Time.now. È giusto questo approccio?

Aggiornamento: Entrambe le soluzioni (a) Time.is e (b) Time.stubs (: ora) .returns (t) di lavoro. (a) è un approccio molto bello ma (b) la soluzione sarà più coerente con altri codici di test.

A questo question un autore chiede una soluzione generale. Per Ruby, nella mia opzione due soluzioni di cui sopra sono simpler e quindi meglio di estrarre il codice che ottiene data/ora corrente.

BTW vi consiglio di usare Chronic per ottenere il tempo desiderato, per esempio

require 'Chronic'  
mon = Chronic.parse("next week monday") 
Time.stubs(:now).returns(mon) 
+1

Perché questa comunità wiki? – Elie

+0

Perché non può essere wiki della comunità? –

+1

Può, ma con la maggior parte delle domande di programmazione specifiche, non è fatto in modo che le persone possano ottenere la reputazione per questo. Non ho una risposta, quindi non mi riguarda, ma ero curioso. – Elie

risposta

3

Mocking Time.now o Date.today sembra abbastanza semplice, sarebbe simile:

require 'rubygems' 
require 'test/unit' 
require 'mocha' 

class MyClass 

    def foo 
    Time.now 
    end 

end 

class MyTest < Test::Unit::TestCase 

    def test_foo 
    assert true 
    t = Time.now 
    Time.expects(:now).returns(t) 
    assert_equal t, MyClass.new.foo 
    end 

end 
3

passare un ITimeProvider (per esempio) di classe alla vostra routine da utilizzare per ottenere il tempo, allora si può prendere in giro e utilizzare l'oggetto fittizio per sempre dare un tempo coerente per la routine da utilizzare.

3

Diritto al largo della parte superiore della mia testa, direi che l'approccio migliore per questo sarebbe di non lasciare che il vostro oggetto ottenere il tempo stesso. In altre parole, passa la data/ora in qualunque metodo venga chiamato sull'oggetto che sta usando i costrutti del tempo built-in al momento. Questo potrebbe, a seconda delle circostanze, essere una soluzione molto più semplice di quella di prendere in giro Date.today e Time.now, come suggerisci.

Edit: lo dico in netto contrasto con il suggerimento di avere un'interfaccia ITimeProvider che si passa da una dipendenza ... questo è solo eccessivo, a mio parere.

0

Creazione di un oggetto ITimeProvider come una dipendenza è meglio che passare il tempo in quanto si inseriscono il principio Non Repeat Yourself.

Da qualche parte nel codice di produzione, qualcosa deve ottenere l'ora corrente. Puoi prendere il codice di generazione della data al di fuori dei confini della copertura del test o puoi avere un singolo oggetto facilmente verificabile che può essere ovunque.

+0

In genere, ottenere l'ora corrente è una singola espressione, ad es. nuovo Date() in Java. L'astrazione in un'interfaccia è un'ingegneria assurda. Probabilmente finirai per ripeterti ancora più di prima. –

+0

Non è certo per ogni progetto. Prenderò in considerazione solo una cosa del genere se dipenderei pesantemente dal tempo. –

+0

Stai dicendo che c'è una violazione DRY perché sia ​​il codice di produzione che il codice di test generano il tempo? In tal caso, in che modo sostituirlo con un aiuto fittizio? Inoltre, c'è una parola mancante in "oppure puoi avere un singolo oggetto facilmente verificabile che può essere ovunque"? –

6

Quanto segue è da Jay Field's Thoughts. Ti consente di ridefinire Time.now per la durata di un blocco.

require 'time' 

class Time 
    def self.metaclass 
    class << self; self; end 
    end 

    def self.is(point_in_time) 
    new_time = case point_in_time 
     when String then Time.parse(point_in_time) 
     when Time then point_in_time 
     else raise ArgumentError.new("argument should be a string or time instance") 
    end 
    class << self 
     alias old_now now 
    end 
    metaclass.class_eval do 
     define_method :now do 
     new_time 
     end 
    end 
    yield 
    class << self 
     alias now old_now 
     undef old_now 
    end 
    end 
end 

Time.is(Time.now) do 
    Time.now # => Tue Nov 13 19:31:46 -0500 2007 
    sleep 2 
    Time.now # => Tue Nov 13 19:31:46 -0500 2007 
end 

Time.is("10/05/2006") do 
    Time.now # => Thu Oct 05 00:00:00 -0400 2006 
    sleep 2 
    Time.now # => Thu Oct 05 00:00:00 -0400 2006 
end