Zu diesen Antworten: Was ist mit Multi-Assertions in einer Testfunktion, und ich erwarte nur eine Ausnahme von einem Wurf? MUSS ich sie trennen und in eine unabhängige Testfunktion versetzen?
Panwen Wang
Antworten:
549
<?php
require_once 'PHPUnit/Framework.php';classExceptionTestextendsPHPUnit_Framework_TestCase{publicfunction testException(){
$this->expectException(InvalidArgumentException::class);// or for PHPUnit < 5.2// $this->setExpectedException(InvalidArgumentException::class);//...and then add your test code that generates the exception
exampleMethod($anInvalidArgument);}}
Wenn Sie Namespaces verwenden, müssen Sie den vollständigen Namespace $this->setExpectedException('\My\Name\Space\MyCustomException');
eingeben
15
Die Tatsache, dass Sie nicht die genaue Codezeile angeben können, die erwartet wird, ist ein Fehler IMO. Und die Unfähigkeit, im selben Test auf mehr als eine Ausnahme zu testen, macht das Testen auf viele erwartete Ausnahmen zu einer wirklich klobigen Angelegenheit. Ich habe eine tatsächliche Behauptung geschrieben , um zu versuchen, diese Probleme zu lösen.
Was ist nicht in der Dokumentation erwähnt oder hier, aber der Code erwartet eine Ausnahme Bedürfnisse zu werfen aufgerufen werden , nachexpectException() . Während es für manche offensichtlich gewesen sein mag, war es ein Gotcha für mich.
Jason McCreary
7
Es ist aus dem Dokument nicht ersichtlich, aber nach Ihrer Funktion, die eine Ausnahme auslöst, wird kein Code ausgeführt. Wenn Sie also mehrere Ausnahmen im selben Testfall testen möchten, können Sie dies nicht.
@LeviMorrison - IMHO die Ausnahme Nachricht sollte nicht getestet werden, ähnlich Nachrichten zu protokollieren. Beide werden bei der Durchführung der manuellen Forensik als irrelevante Informationen angesehen . Der wichtigste zu testende Punkt ist die Art der Ausnahme. Alles darüber hinaus ist zu eng an die Implementierung gebunden. IncorrectPasswordExceptionsollte ausreichen - dass die Nachricht gleich "Wrong password for [email protected]"ist, ist ergänzend. Fügen Sie hinzu, dass Sie so wenig Zeit wie möglich mit dem Schreiben von Tests verbringen möchten, und Sie beginnen zu sehen, wie wichtig einfache Tests werden.
David Harkness
5
@ DavidHarkness Ich dachte, jemand würde das ansprechen. Ebenso würde ich zustimmen, dass das Testen von Nachrichten im Allgemeinen zu streng und streng ist. Es ist jedoch diese Strenge und enge Bindung, die (absichtlich hervorgehoben) in einigen Situationen, wie der Durchsetzung einer Spezifikation, gewünscht sein kann.
Levi Morrison
1
Ich würde nicht in einem Dokumentblock nachsehen, um zu verstehen, was erwartet wird, aber ich würde mir den tatsächlichen Testcode ansehen (unabhängig von der Art des Tests). Das ist der Standard für alle anderen Tests. Ich sehe keine triftigen Gründe dafür, dass Ausnahmen (oh Gott) eine Ausnahme von dieser Konvention sind.
Kamafeather
3
Die Regel "Nachricht nicht testen" klingt gültig, es sei denn, Sie testen eine Methode, die denselben Ausnahmetyp in mehreren Codeteilen auslöst. Der einzige Unterschied besteht in der Fehler-ID, die in der Nachricht übergeben wird. Ihr System zeigt dem Benutzer möglicherweise eine Nachricht basierend auf der Ausnahmemeldung an (nicht dem Ausnahmetyp). In diesem Fall spielt es eine Rolle, welche Meldung der Benutzer sieht. Daher sollten Sie die Fehlermeldung testen.
Der folgende Code testet die Ausnahmemeldung und den Ausnahmecode.
Wichtig: Es schlägt fehl, wenn die erwartete Ausnahme nicht ebenfalls ausgelöst wird.
try{
$test->methodWhichWillThrowException();//if this method not throw exception it must be fail too.
$this->fail("Expected exception 1162011 not thrown");}catch(MySpecificException $e){//Not catching a generic Exception or the fail function is also catched
$this->assertEquals(1162011, $e->getCode());
$this->assertEquals("Exception Message", $e->getMessage());}
$this->fail()soll nicht so verwendet werden, glaube ich nicht, zumindest momentan nicht (PHPUnit 3.6.11); es fungiert selbst als Ausnahme. Mit Ihrem Beispiel, wenn $this->fail("Expected exception not thrown")aufgerufen wird, dann wird der catchBlock wird ausgelöst und $e->getMessage()wird „Erwartet keine Ausnahme ausgelöst“ .
Ken
1
@ken du hast wahrscheinlich recht. Der Aufruf von failgehört wahrscheinlich nach dem catch-Block, nicht innerhalb des Try.
Frank Farmer
1
Ich muss abstimmen, weil der Anruf an failnicht im tryBlock sein sollte. Es löst an sich den catchBlock aus, der falsche Ergebnisse erzeugt.
Twifty
6
Ich glaube, der Grund, warum dies nicht gut funktioniert, ist, dass in einigen Situationen alle Ausnahmen erfasst werden catch(Exception $e). Diese Methode funktioniert ganz gut für mich, wenn ich versuche, bestimmte Ausnahmen zu fangen:try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
Fügen Sie die Methode in Ihren TestCase ein und verwenden Sie:
publicfunction testSomething(){
$test =function(){// some code that has to throw an exception};
$this->assertException( $test,'InvalidArgumentException',100,'expected message');}
Ich habe auch ein Merkmal für Liebhaber von nettem Code gemacht.
Welche PHPUnit verwenden Sie? Ich verwende PHPUnit 4.7.5 und es assertExceptionist nicht definiert. Ich kann es auch nicht im PHPUnit-Handbuch finden.
körperliche
2
Die asertExceptionMethode ist nicht Teil der ursprünglichen PHPUnit. Sie müssen die PHPUnit_Framework_TestCaseKlasse erben und die im obigen Beitrag verknüpfte Methode manuell hinzufügen . Ihre Testfälle erben dann diese geerbte Klasse.
Die PHPUnit- expectExceptionMethode ist sehr unpraktisch, da nur eine Ausnahme pro Testmethode getestet werden kann.
Ich habe diese Hilfsfunktion erstellt, um zu behaupten, dass eine Funktion eine Ausnahme auslöst:
/**
* Asserts that the given callback throws the given exception.
*
* @param string $expectClass The name of the expected exception class
* @param callable $callback A callback which should throw the exception
*/protectedfunction assertException(string $expectClass, callable $callback){try{
$callback();}catch(\Throwable $exception){
$this->assertInstanceOf($expectClass, $exception,'An invalid exception was thrown');return;}
$this->fail('No exception was thrown');}
Fügen Sie es Ihrer Testklasse hinzu und rufen Sie folgendermaßen auf:
Auf jeden Fall die beste Lösung aus allen Antworten! Wirf es in eine Eigenschaft und verpacke es!
Domdambrogia
11
Umfassende Lösung
Die aktuellen " Best Practices " von PHPUnit für Ausnahmetests scheinen .. glanzlos ( docs ).
Da ich mehr als die aktuelle expectExceptionImplementierung wollte, habe ich eine Eigenschaft für meine Testfälle erstellt. Es sind nur ~ 50 Codezeilen .
Unterstützt mehrere Ausnahmen pro Test
Unterstützt Zusicherungen, die aufgerufen werden, nachdem die Ausnahme ausgelöst wurde
Robuste und klare Anwendungsbeispiele
Standard assert Syntax
Unterstützt Zusicherungen für mehr als nur Nachricht, Code und Klasse
Nur um den Geist hinter der Syntax zu veranschaulichen:
<?php
// Using simple callback
$this->assertThrows(MyException::class,[$obj,'doSomethingBad']);// Using anonymous function
$this->assertThrows(MyException::class,function()use($obj){
$obj->doSomethingBad();});
Ziemlich ordentlich?
Beispiel für die vollständige Verwendung
Im Folgenden finden Sie ein umfassenderes Anwendungsbeispiel:
<?php
declare(strict_types=1);useJchook\AssertThrows\AssertThrows;usePHPUnit\Framework\TestCase;// These are just for illustrationuseMyNamespace\MyException;useMyNamespace\MyObject;finalclassMyTestextendsTestCase{useAssertThrows;// <--- adds the assertThrows methodpublicfunction testMyObject(){
$obj =newMyObject();// Test a basic exception is thrown
$this->assertThrows(MyException::class,function()use($obj){
$obj->doSomethingBad();});// Test custom aspects of a custom extension class
$this->assertThrows(MyException::class,function()use($obj){
$obj->doSomethingBad();},function($exception){
$this->assertEquals('Expected value', $exception->getCustomThing());
$this->assertEquals(123, $exception->getCode());});// Test that a specific exception is NOT thrown
$this->assertNotThrows(MyException::class,function()use($obj){
$obj->doSomethingGood();});}}?>
publicfunction testException(){try{
$this->methodThatThrowsException();
$this->fail("Expected Exception has not been raised.");}catch(Exception $ex){
$this->assertEquals($ex->getMessage(),"Exception message");}}
Die Unterschrift von assertEquals()ist assertEquals(mixed $expected, mixed $actual...), umgekehrt wie in Ihrem Beispiel, so sollte es sein$this->assertEquals("Exception message", $ex->getMessage());
Roger Campanera
7
Hier finden Sie alle Ausnahmen, die Sie ausführen können. Beachten Sie, dass alle optional sind .
classExceptionTestextendsPHPUnit_Framework_TestCase{publicfunction testException(){// make your exception assertions
$this->expectException(InvalidArgumentException::class);// if you use namespaces:// $this->expectException('\Namespace\MyException');
$this->expectExceptionMessage('message');
$this->expectExceptionMessageRegExp('/essage$/');
$this->expectExceptionCode(123);// code that throws an exceptionthrownewInvalidArgumentException('message',123);}publicfunction testAnotherException(){// repeat as needed
$this->expectException(Exception::class);thrownewException('Oh no!');}}
Es ist falsch, weil PHP bei der ersten ausgelösten Ausnahme stoppt. PHPUnit prüft, ob die ausgelöste Ausnahme den richtigen Typ hat und sagt «der Test ist in Ordnung», es weiß nicht einmal über die zweite Ausnahme Bescheid.
Finesse
3
/**
* @expectedException Exception
* @expectedExceptionMessage Amount has to be bigger then 0!
*/publicfunction testDepositNegative(){
$this->account->deposit(-7);}
Seien Sie sehr vorsichtig "/**", beachten Sie das doppelte "*". Wenn Sie nur "**" (Sternchen) schreiben, schlägt Ihr Code fehl. Stellen Sie außerdem sicher, dass Sie die letzte Version von phpUnit verwenden. In einigen früheren Versionen von phpunit @expectedException wird Exception nicht unterstützt. Ich hatte 4.0 und es funktionierte nicht für mich. Ich musste auf 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer aktualisieren, um mit Composer zu aktualisieren.
Für PHPUnit 5.7.27 und PHP 5.6 und um mehrere Ausnahmen in einem Test zu testen, war es wichtig, den Ausnahmetest zu erzwingen. Wenn Sie nur die Ausnahmebehandlung verwenden, um die Instanz der Ausnahme zu aktivieren, wird das Testen der Situation übersprungen, wenn keine Ausnahme auftritt.
function yourfunction($a,$z){if($a<$z){thrownew<YOUR_EXCEPTION>;}}
Hier ist der Test
classFunctionTestextends \PHPUnit_Framework_TestCase{publicfunction testException(){
$this->setExpectedException(<YOUR_EXCEPTION>::class);
yourfunction(1,2);//add vars that cause the exception }}
PhpUnit ist eine erstaunliche Bibliothek, aber dieser spezielle Punkt ist ein bisschen frustrierend. Aus diesem Grund können wir die OpenSource-Bibliothek turbotesting-php verwenden, die über eine sehr praktische Assertionsmethode verfügt, mit der wir Ausnahmen testen können. Es ist hier zu finden:
Und um es zu verwenden, würden wir einfach Folgendes tun:
AssertUtils::throwsException(function(){// Some code that must throw an exception here},'/expected error message/');
Wenn der Code, den wir in die anonyme Funktion eingeben, keine Ausnahme auslöst, wird eine Ausnahme ausgelöst.
Wenn der Code, den wir in die anonyme Funktion eingeben, eine Ausnahme auslöst, die Nachricht jedoch nicht mit dem erwarteten regulären Ausdruck übereinstimmt, wird auch eine Ausnahme ausgelöst.
Antworten:
erwartete EXception () PHPUnit-Dokumentation
Der Artikel des PHPUnit-Autors enthält detaillierte Erläuterungen zum Testen der Best Practices für Ausnahmen.
quelle
$this->setExpectedException('\My\Name\Space\MyCustomException');
setExpectedException
Methode veraltet und wird durch dieexpectException
ersetzt. :)expectException()
. Während es für manche offensichtlich gewesen sein mag, war es ein Gotcha für mich.Sie können auch eine Docblock-Annotation verwenden, bis PHPUnit 9 veröffentlicht wird:
Für PHP 5.5+ (insbesondere mit Namespace-Code) bevorzuge ich jetzt die Verwendung
::class
quelle
IncorrectPasswordException
sollte ausreichen - dass die Nachricht gleich"Wrong password for [email protected]"
ist, ist ergänzend. Fügen Sie hinzu, dass Sie so wenig Zeit wie möglich mit dem Schreiben von Tests verbringen möchten, und Sie beginnen zu sehen, wie wichtig einfache Tests werden.Wenn Sie mit PHP 5.5+ arbeiten, können Sie die
::class
Auflösung verwenden , um den Namen der Klasse mitexpectException
/setExpectedException
abzurufen . Dies bietet mehrere Vorteile:string
sodass es mit jeder Version von PHPUnit funktioniert.Beispiel:
PHP kompiliert
in
ohne dass PHPUnit klüger ist.
quelle
Der folgende Code testet die Ausnahmemeldung und den Ausnahmecode.
Wichtig: Es schlägt fehl, wenn die erwartete Ausnahme nicht ebenfalls ausgelöst wird.
quelle
$this->fail()
soll nicht so verwendet werden, glaube ich nicht, zumindest momentan nicht (PHPUnit 3.6.11); es fungiert selbst als Ausnahme. Mit Ihrem Beispiel, wenn$this->fail("Expected exception not thrown")
aufgerufen wird, dann wird dercatch
Block wird ausgelöst und$e->getMessage()
wird „Erwartet keine Ausnahme ausgelöst“ .fail
gehört wahrscheinlich nach dem catch-Block, nicht innerhalb des Try.fail
nicht imtry
Block sein sollte. Es löst an sich dencatch
Block aus, der falsche Ergebnisse erzeugt.catch(Exception $e)
. Diese Methode funktioniert ganz gut für mich, wenn ich versuche, bestimmte Ausnahmen zu fangen:try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
Mit der Erweiterung assertException können Sie während einer Testausführung mehr als eine Ausnahme aktivieren .
Fügen Sie die Methode in Ihren TestCase ein und verwenden Sie:
Ich habe auch ein Merkmal für Liebhaber von nettem Code gemacht.
quelle
assertException
ist nicht definiert. Ich kann es auch nicht im PHPUnit-Handbuch finden.asertException
Methode ist nicht Teil der ursprünglichen PHPUnit. Sie müssen diePHPUnit_Framework_TestCase
Klasse erben und die im obigen Beitrag verknüpfte Methode manuell hinzufügen . Ihre Testfälle erben dann diese geerbte Klasse.Ein alternativer Weg kann der folgende sein:
Bitte stellen Sie sicher, dass Ihre Testklasse erweitert ist
\PHPUnit_Framework_TestCase
.quelle
Die PHPUnit-
expectException
Methode ist sehr unpraktisch, da nur eine Ausnahme pro Testmethode getestet werden kann.Ich habe diese Hilfsfunktion erstellt, um zu behaupten, dass eine Funktion eine Ausnahme auslöst:
Fügen Sie es Ihrer Testklasse hinzu und rufen Sie folgendermaßen auf:
quelle
Umfassende Lösung
Die aktuellen " Best Practices " von PHPUnit für Ausnahmetests scheinen .. glanzlos ( docs ).
Da ich mehr als die aktuelle
expectException
Implementierung wollte, habe ich eine Eigenschaft für meine Testfälle erstellt. Es sind nur ~ 50 Codezeilen .assert
SyntaxassertNotThrows
Throwable
FehlerBibliothek
Ich habe das
AssertThrows
Merkmal für Github und Packagist veröffentlicht, damit es mit Composer installiert werden kann.Einfaches Beispiel
Nur um den Geist hinter der Syntax zu veranschaulichen:
Ziemlich ordentlich?
Beispiel für die vollständige Verwendung
Im Folgenden finden Sie ein umfassenderes Anwendungsbeispiel:
quelle
quelle
assertEquals()
istassertEquals(mixed $expected, mixed $actual...)
, umgekehrt wie in Ihrem Beispiel, so sollte es sein$this->assertEquals("Exception message", $ex->getMessage());
Hier finden Sie alle Ausnahmen, die Sie ausführen können. Beachten Sie, dass alle optional sind .
Dokumentation finden Sie hier .
quelle
Seien Sie sehr vorsichtig
"/**"
, beachten Sie das doppelte "*". Wenn Sie nur "**" (Sternchen) schreiben, schlägt Ihr Code fehl. Stellen Sie außerdem sicher, dass Sie die letzte Version von phpUnit verwenden. In einigen früheren Versionen von phpunit @expectedException wird Exception nicht unterstützt. Ich hatte 4.0 und es funktionierte nicht für mich. Ich musste auf 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer aktualisieren, um mit Composer zu aktualisieren.quelle
Für PHPUnit 5.7.27 und PHP 5.6 und um mehrere Ausnahmen in einem Test zu testen, war es wichtig, den Ausnahmetest zu erzwingen. Wenn Sie nur die Ausnahmebehandlung verwenden, um die Instanz der Ausnahme zu aktivieren, wird das Testen der Situation übersprungen, wenn keine Ausnahme auftritt.
quelle
Hier ist der Test
quelle
PhpUnit ist eine erstaunliche Bibliothek, aber dieser spezielle Punkt ist ein bisschen frustrierend. Aus diesem Grund können wir die OpenSource-Bibliothek turbotesting-php verwenden, die über eine sehr praktische Assertionsmethode verfügt, mit der wir Ausnahmen testen können. Es ist hier zu finden:
https://github.com/edertone/TurboTesting/blob/master/TurboTesting-Php/src/main/php/utils/AssertUtils.php
Und um es zu verwenden, würden wir einfach Folgendes tun:
Wenn der Code, den wir in die anonyme Funktion eingeben, keine Ausnahme auslöst, wird eine Ausnahme ausgelöst.
Wenn der Code, den wir in die anonyme Funktion eingeben, eine Ausnahme auslöst, die Nachricht jedoch nicht mit dem erwarteten regulären Ausdruck übereinstimmt, wird auch eine Ausnahme ausgelöst.
quelle