... nicht zu wissen, ob 'mock' das richtige Wort ist.
Wie auch immer, ich habe eine geerbte Codebasis, für die ich einige Tests schreiben möchte, die zeitbasiert sind. Um nicht zu vage zu sein, bezieht sich der Code darauf, den Verlauf eines Elements zu betrachten und festzustellen, ob dieses Element jetzt einen Zeitschwellenwert hat.
Irgendwann muss ich auch testen, ob ich etwas zu diesem Verlauf hinzufüge und überprüfe, ob der Schwellenwert jetzt geändert wurde (und natürlich korrekt ist).
Das Problem, auf das ich stoße, ist, dass ein Teil des Codes, den ich teste, Aufrufe von time () verwendet. Daher fällt es mir wirklich schwer, genau zu wissen, wie hoch die Schwellenzeit sein sollte, basierend auf der Tatsache, dass ich ' Ich bin mir nicht ganz sicher, wann genau diese time () -Funktion aufgerufen wird.
Meine Frage lautet also im Grunde: Gibt es eine Möglichkeit für mich, den Aufruf von time () zu "überschreiben" oder die Zeit irgendwie zu "verspotten", sodass meine Tests in einer "bekannten Zeit" funktionieren?
Oder muss ich einfach akzeptieren, dass ich in dem Code, den ich teste, etwas tun muss, damit ich ihn bei Bedarf zwingen kann, eine bestimmte Zeit zu verwenden?
Gibt es in beiden Fällen gängige Vorgehensweisen für die Entwicklung zeitkritischer Funktionen, die testfreundlich sind?
Bearbeiten: Ein Teil meines Problems ist auch die Tatsache, dass die Zeit, zu der Dinge in der Geschichte auftraten, die Schwelle beeinflusst. Hier ist ein Beispiel für einen Teil meines Problems ...
Stellen Sie sich vor, Sie haben eine Banane und versuchen herauszufinden, wann sie gegessen werden muss. Nehmen wir an, dass es innerhalb von 3 Tagen abläuft, es sei denn, es wurde mit einer Chemikalie besprüht. In diesem Fall addieren wir 4 Tage zum Verfallsdatum ab dem Zeitpunkt , an dem das Spray angewendet wurde . Dann können wir weitere 3 Monate hinzufügen, indem wir es einfrieren. Wenn es jedoch gefroren ist, haben wir nur 1 Tag Zeit, um es nach dem Auftauen zu verwenden.
Alle diese Regeln werden von historischen Zeitpunkten bestimmt. Ich bin damit einverstanden, dass ich den Vorschlag des Dominik, innerhalb weniger Sekunden zu testen, nutzen kann, aber was ist mit meinen historischen Daten? Sollte ich das einfach im laufenden Betrieb "erstellen"?
Wie Sie vielleicht oder vielleicht nicht sagen können, versuche ich immer noch, all dieses 'Test'-Konzept in den Griff zu bekommen;)
Antworten:
Ich habe kürzlich eine andere Lösung gefunden, die großartig ist, wenn Sie PHP 5.3-Namespaces verwenden. Sie können eine neue time () - Funktion in Ihrem aktuellen Namespace implementieren und eine gemeinsam genutzte Ressource erstellen, in der Sie den Rückgabewert in Ihren Tests festlegen. Dann verwendet jeder unqualifizierte Aufruf von time () Ihre neue Funktion.
Zur weiteren Lektüre habe ich es in meinem Blog ausführlich beschrieben
quelle
time()
.Für diejenigen von Ihnen mit symfony arbeiten (> = 2.8): Symfony der PHPUnit - Brücke enthält eine ClockMock - Funktion , die die integrierten Methoden überschreibt
time
,microtime
,sleep
undusleep
.Siehe: http://symfony.com/doc/2.8/components/phpunit_bridge.html#clock-mocking
quelle
Sie können die Testzeit mit Clock von Ouzo-Goodies verspotten .
Verwenden Sie im Code einfach:
Dann in Tests:
Clock::freeze('2014-01-07 12:34'); $result = Class::getCurrDate(); $this->assertEquals('2014-01-07', $result);
quelle
Ich musste eine bestimmte Anfrage in Zukunft und in der Vergangenheit in der App selbst simulieren (nicht in Unit Tests). Daher sollten alle Aufrufe von \ DateTime :: now () das Datum zurückgeben, das zuvor in der gesamten App festgelegt wurde.
Ich habe mich für diese Bibliothek https://github.com/rezzza/TimeTraveler entschieden , da ich die Daten verspotten kann, ohne alle Codes zu ändern.
\Rezzza\TimeTraveler::enable(); \Rezzza\TimeTraveler::moveTo('2011-06-10 11:00:00'); var_dump(new \DateTime()); // 2011-06-10 11:00:00 var_dump(new \DateTime('+2 hours')); // 2011-06-10 13:00:00
quelle
new \DateTime()
in ihrem Code verwenden. Aber wie soll das installiert werden? Keine Infos im Github Repo.Persönlich verwende ich weiterhin time () in den getesteten Funktionen / Methoden. Stellen Sie in Ihrem Testcode nur sicher, dass Sie nicht auf Gleichheit mit time () testen, sondern nur auf einen Zeitunterschied von weniger als 1 oder 2 (je nachdem, wie viel Zeit die Funktion für die Ausführung benötigt).
quelle
Carbon::setTestNow(Carbon $time = null)
ruft zur gleichen Zeit anCarbon::now()
odernew Carbon('now')
kehrt zurück.https://medium.com/@stefanledin/mock-date-and-time-with-carbon-8a9f72cb843d
Beispiel:
public function testSomething() { $now = Carbon::now(); // Mock Carbon::now() / new Carbon('now') to always return the same time Carbon::setTestNow($now); // Do the time sensitive test: $this->retroEncabulator('prefabulate') ->assertJsonFragment(['whenDidThisHappen' => $now->timestamp]) // Release the Carbon::now() mock Carbon::setTestNow(); }
Die
$this->retroEncabulator()
Funktion muss natürlichCarbon::now()
odernew Carbon('now')
intern verwendet werden.quelle
$now = Carbon::now(); Carbon::setTestNow($now);
Sie können die time () - Funktion von php mit der runkit-Erweiterung überschreiben. Stellen Sie sicher, dass Sie runkit.internal_overide auf On setzen
quelle
Verwenden der Erweiterung [runkit] [1]:
define('MOCK_DATE', '2014-01-08'); define('MOCK_TIME', '17:30:00'); define('MOCK_DATETIME', MOCK_DATE.' '.MOCK_TIME); private function mockDate() { runkit_function_rename('date', 'date_real'); runkit_function_add('date','$format="Y-m-d H:i:s", $timestamp=NULL', '$ts = $timestamp ? $timestamp : strtotime(MOCK_DATETIME); return date_real($format, $ts);'); } private function unmockDate() { runkit_function_remove('date'); runkit_function_rename('date_real', 'date'); }
Sie können den Mock sogar so testen:
public function testMockDate() { $this->mockDate(); $this->assertEquals(MOCK_DATE, date('Y-m-d')); $this->assertEquals(MOCK_TIME, date('H:i:s')); $this->assertEquals(MOCK_DATETIME, date()); $this->unmockDate(); }
quelle
In den meisten Fällen reicht dies aus. Es hat einige Vorteile:
Es wird phpunit verwendet, aber Sie können es an jedes andere Testframework anpassen. Sie benötigen lediglich eine Funktion, die wie assertContains () von phpunit funktioniert.
1) Fügen Sie Ihrer Testklasse oder Ihrem Bootstrap die folgende Funktion hinzu. Die Standardtoleranz für die Zeit beträgt 2 Sekunden. Sie können es ändern, indem Sie das dritte Argument an assertTimeEquals übergeben oder Funktionsargumente ändern.
private function assertTimeEquals($testedTime, $shouldBeTime, $timeTolerance = 2) { $toleranceRange = range($shouldBeTime, $shouldBeTime+$timeTolerance); return $this->assertContains($testedTime, $toleranceRange); }
2) Testbeispiel:
public function testGetLastLogDateInSecondsAgo() { // given $date = new DateTime(); $date->modify('-189 seconds'); // when $this->setLastLogDate($date); // then $this->assertTimeEquals(189, $this->userData->getLastLogDateInSecondsAgo()); }
assertTimeEquals () prüft, ob das Array von (189, 190, 191) 189 enthält.
Dieser Test sollte für die korrekte Arbeitsfunktion bestanden werden, wenn die Ausführung der Testfunktion weniger als 2 Sekunden dauert.
Es ist nicht perfekt und sehr genau, aber es ist sehr einfach und in vielen Fällen reicht es aus, um zu testen, was Sie testen möchten.
quelle
Die einfachste Lösung wäre, die PHP time () -Funktion zu überschreiben und durch Ihre eigene Version zu ersetzen. Sie können integrierte PHP-Funktionen jedoch nicht einfach ersetzen ( siehe hier ).
Abgesehen davon besteht die einzige Möglichkeit darin, den Aufruf von time () an eine eigene Klasse / Funktion zu abstrahieren, die die zum Testen benötigte Zeit zurückgibt.
Alternativ können Sie das Testsystem (Betriebssystem) in einer virtuellen Maschine ausführen und die Zeit des gesamten virtuellen Computers ändern.
quelle
Hier ist eine Ergänzung zu Fabs Beitrag. Ich habe die Namespace-basierte Überschreibung mit einer Auswertung durchgeführt. Auf diese Weise kann ich es nur für Tests ausführen und nicht den Rest meines Codes. Ich führe eine ähnliche Funktion aus:
function timeOverrides($namespaces = array()) { $returnTime = time(); foreach ($namespaces as $namespace) { eval("namespace $namespace; function time() { return $returnTime; }"); } }
Übergeben Sie dann
timeOverrides(array(...))
das Test-Setup, damit meine Tests nur verfolgen müssen, in welchen Namespaces time () aufgerufen wird.quelle