webentwicklung-frage-antwort-db.com.de

Wie man Time.now fälscht?

Was ist der beste Weg, Time.now einzustellen, um zeitempfindliche Methoden in einem Komponententest zu testen?

68
user147962

Ich mag die Timecop - Bibliothek wirklich. Sie können Zeitverzerrungen in Blockform durchführen (genau wie Zeitverzerrungen):

Timecop.travel(6.days.ago) do
  @model = TimeSensitiveMode.new
end
assert @model.times_up!

(Ja, Sie können Blockzeit-Zeitreisen verschachteln.)

Sie können auch deklarative Zeitreisen durchführen:

class MyTest < Test::Unit::TestCase
  def setup
    Timecop.travel(...)
  end
  def teardown
    Timecop.return
  end
end

Ich habe einige Gurken Helfer für Timecop hier . Sie lassen Sie Dinge tun wie:

Given it is currently January 24, 2008
And I go to the new post page
And I fill in "title" with "An old post"
And I fill in "body" with "..."
And I press "Submit"
And we jump in our Delorean and return to the present
When I go to the home page
I should not see "An old post"
72
James A. Rosen

Ich persönlich bevorzuge die Uhr injizierbar zu machen:

def hello(clock=Time)
  puts "the time is now: #{clock.now}"
end

Oder:

class MyClass
  attr_writer :clock

  def initialize
    @clock = Time
  end

  def hello
    puts "the time is now: #{@clock.now}"
  end
end

Viele ziehen es jedoch vor, eine spöttische Bibliothek zu verwenden. In RSpec/Flexmock können Sie Folgendes verwenden:

Time.stub!(:now).and_return(Time.mktime(1970,1,1))

Oder in Mocha:

Time.stubs(:now).returns(Time.mktime(1970,1,1))
42
Avdi

Ich benutze RSpec und habe folgendes gemacht: Time.stub! (: Now) .and_return (2.days.ago) bevor ich Time.now anrufe. Auf diese Weise kann ich die Zeit steuern, die ich für diesen bestimmten Testfall verwendet habe

14
Staelen

Machen Sie die Zeitverzerrung

time-Warp ist eine Bibliothek, die macht, was Sie wollen. Es gibt eine Methode, die eine Zeit und einen Block benötigt, und alles, was in dem Block passiert, verwendet die gefälschte Zeit.

pretend_now_is(2000,"jan",1,0) do
  Time.now
end
10
BaroqueBobcat

Bei Verwendung von Rspec 3.2 habe ich den einzigen einfachen Weg gefunden, den Time.now-Rückgabewert zu fälschen:

now = Time.parse("1969-07-20 20:17:40")
allow(Time).to receive(:now) { now }

Jetzt gibt Time.now immer das Datum der Landung von Apollo 11 auf dem Mond zurück.

Quelle: https://www.relishapp.com/rspec/rspec-mocks/docs

8
Iwazaru

Vergessen Sie nicht, dass Time nur eine Konstante ist, die auf ein Klassenobjekt verweist. Wenn Sie eine Warnung auslösen möchten, können Sie dies jederzeit tun

real_time_class = Time
Time = FakeTimeClass
# run test
Time = real_time_class
7
Andrew Grimm

Siehe auch diese Frage wo ich diesen Kommentar auch platziere.

Je nachdem, was Sie mit Time.now vergleichen, können Sie manchmal Ihre Fixtures ändern, um dasselbe Ziel zu erreichen, oder das gleiche Feature testen. Ich hatte zum Beispiel eine Situation, in der ich eine Sache brauchte, wenn ein Datum in der Zukunft lag und eine andere, wenn es in der Vergangenheit lag. Was ich konnte, war eingebettetes Ruby (erb):

future:
    comparing_date: <%= Time.now + 10.years %>
    ...

past:
    comparing_date: <%= Time.now - 10.years %>
    ...

In Ihren Tests wählen Sie dann aus, welche der verschiedenen Funktionen oder Aktionen basierend auf der Zeit relativ zu Time.now getestet werden soll.

1
Bryan Ward

Hatte das gleiche Problem, ich musste die Zeit für eine Spezifikation für einen bestimmten Tag fälschen, und die Zeit tat genau das:

Time.stub!(:now).and_return(Time.mktime(2014,10,22,5,35,28))        

das wird dir geben:

2014-10-22 05:35:28 -0700
1
Netta D

Diese Art von Arbeiten und ermöglicht das Schachteln: 

class Time
  class << self
    attr_accessor :stack, :depth
  end

  def self.warp(time)

    Time.stack ||= [] 
    Time.depth ||= -1 
    Time.depth += 1
    Time.stack.Push time

    if Time.depth == 0 
      class << self    
          alias_method :real_now, :now  
          alias_method :real_new, :new

          define_method :now do
            stack[depth] 
          end

          define_method :new do 
            now 
          end
      end 
    end 

    yield

    Time.depth -= 1
    Time.stack.pop 

    class << self 
      if Time.depth < 0 
        alias_method :new, :real_new
        alias_method :now, :real_now
        remove_method :real_new
        remove_method :real_now 
      end
    end

  end
end

Es könnte etwas verbessert werden, indem am Ende die Stapel- und Tiefenzugriffe entfernt werden

Verwendungszweck: 

time1 = 2.days.ago
time2 = 5.months.ago
Time.warp(time1) do 

  Time.real_now.should_not == Time.now

  Time.now.should == time1 
  Time.warp(time2) do 
    Time.now.should == time2
  end 
  Time.now.should == time1
end

Time.now.should_not == time1 
Time.now.should_not be_nil
0
Sam Saffron

ich habe dies nur in meiner Testdatei:

   def time_right_now
      current_time = Time.parse("07/09/10 14:20")
      current_time = convert_time_to_utc(current_date)
      return current_time
    end

und in meiner Time_helper.rb-Datei habe ich eine 

  def time_right_now
    current_time= Time.new
    return current_time
  end

beim Testen wird time_right_now überschrieben, um die Uhrzeit zu verwenden, die Sie möchten. 

0
Mo.

Wenn Sie ActiveSupport verwenden, können Sie http://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html verwenden.

0
Artur Beljajev

Ich extrahiere Time.now immer in eine separate Methode, die ich im Mock in attr_accessor umwandle.

0
hakunin

Der kürzlich veröffentlichte Test::Redef macht dieses und andere Fake einfach, auch ohne den Code in einem Abhängigkeits-Injektionsstil umzugestalten (besonders hilfreich, wenn Sie den Code anderer Benutzer verwenden.)

fake_time = Time.at(12345) # ~3:30pm UTC Jan 1 1970
Test::Redef.rd 'Time.now' => proc { fake_time } do
   assert_equal 12345, Time.now.to_i
end

Achten Sie jedoch auf andere Möglichkeiten, um Zeit zu erhalten, die nicht verfälscht wird (Date.new, eine kompilierte Erweiterung, die ein eigenes System aufruft, Schnittstellen zu Dingen wie externen Datenbankservern, die aktuelle Zeitstempel kennen usw.). Es klingt wie die Timecop-Bibliothek oben könnten diese Einschränkungen überwinden.

Andere großartige Anwendungen umfassen das Testen von Dingen wie "Was passiert, wenn ich versuche, diesen benutzerfreundlichen http-Client zu verwenden, aber er beschließt, diese Ausnahme auszulösen, anstatt mir eine Zeichenfolge zurückzugeben?" ohne die Netzbedingungen festzulegen, die zu dieser Ausnahme führen (was schwierig sein kann). Außerdem können Sie die Argumente überprüfen, um Funktionen neu zu definieren.

0
fennec

Je nachdem, was Sie mit Time.now vergleichen, können Sie manchmal Ihre Fixtures ändern, um dasselbe Ziel zu erreichen, oder das gleiche Feature testen. Ich hatte zum Beispiel eine Situation, in der ich eine Sache brauchte, wenn ein Datum in der Zukunft lag und eine andere, wenn es in der Vergangenheit lag. Was ich konnte, war eingebettetes Ruby (erb):

future:
    comparing_date: <%= Time.now + 10.years %>
    ...

past:
    comparing_date: <%= Time.now - 10.years %>
    ...

In Ihren Tests wählen Sie dann aus, welche der verschiedenen Funktionen oder Aktionen basierend auf der Zeit relativ zu Time.now getestet werden soll.

0
Bryan Ward