Na te odpowiedzi: co z wieloma asercjami w funkcji testowej, a ja po prostu oczekuję jednego wyjątku dotyczącego rzucania? Czy muszę je rozdzielić i wprowadzić niezależną funkcję testową?
Panwen Wang
Odpowiedzi:
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);}}
Jeśli korzystasz z przestrzeni nazw, musisz wprowadzić pełną przestrzeń nazw:$this->setExpectedException('\My\Name\Space\MyCustomException');
Alcalyn
15
Fakt, że nie można wyznaczyć dokładnego wiersza kodu, który ma zostać wygenerowany, jest błędem IMO. Brak możliwości przetestowania więcej niż jednego wyjątku w tym samym teście sprawia, że testowanie wielu oczekiwanych wyjątków jest naprawdę niezręczną sprawą. Napisałem rzeczywiste stwierdzenie, aby spróbować rozwiązać te problemy.
mindplay.dk
18
FYI: od phpunit 5.2.0setExpectedException metoda jest przestarzała, zastąpiona tą expectException. :)
hejdav
41
Co nie jest wymienione w docs lub tutaj, ale kod oczekuje się rzucić potrzeb wyjątków być nazywany poexpectException() . Choć dla niektórych mogło to być oczywiste, dla mnie była to gotcha .
Jason McCreary
7
Nie jest to oczywiste z dokumentu, ale żaden kod po twojej funkcji, który zgłasza wyjątek, nie zostanie wykonany. Więc jeśli chcesz przetestować wiele wyjątków w tym samym przypadku testowym, nie możesz.
@LeviMorrison - IMHO komunikat wyjątku nie powinien być testowany, podobnie jak komunikaty dziennika. Oba są uważane za obce, pomocne informacje podczas ręcznej analizy sądowej. Kluczowym punktem do przetestowania jest rodzaj wyjątku. Wszystko poza tym wiąże się zbyt ściśle z implementacją. IncorrectPasswordExceptionpowinno wystarczyć - aby komunikat "Wrong password for [email protected]"był pomocniczy. Dodaj do tego, że chcesz poświęcić jak najmniej czasu na pisanie testów, a zaczniesz widzieć, jak ważne stają się proste testy.
David Harkness
5
@DavidHarkness Pomyślałem, że ktoś to podniesie. Podobnie zgodziłbym się, że testowanie wiadomości ogólnie jest zbyt surowe i ścisłe. Jednak to właśnie ścisłość i ścisłe wiązanie może (celowo podkreślić) być tym, czego pragnie się w niektórych sytuacjach, takich jak egzekwowanie specyfikacji.
Levi Morrison,
1
Nie oglądałbym w bloku dokumentów, żeby zrozumieć, czego się spodziewał, ale patrzyłem na rzeczywisty kod testowy (niezależnie od rodzaju testu). To jest standard dla wszystkich innych testów; Nie widzę ważnych powodów, dla których Wyjątki były (o Boże) wyjątkiem od tej konwencji.
Kamafeather
3
Reguła „nie testuj komunikatu” brzmi poprawnie, chyba że przetestujesz metodę, która generuje ten sam typ wyjątku w wielu częściach kodu, a jedyną różnicą jest identyfikator błędu przekazywany w komunikacie. Twój system może wyświetlać użytkownikowi komunikat na podstawie komunikatu wyjątku (nie typu wyjątku). W takim przypadku nie ma znaczenia, który komunikat widzi użytkownik, dlatego należy przetestować komunikat o błędzie.
Poniższy kod przetestuje komunikat o wyjątku i kod wyjątku.
Ważne: zakończy się niepowodzeniem, jeśli nie zostanie zgłoszony oczekiwany wyjątek.
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()nie powinien być używany w ten sposób, nie sądzę, przynajmniej obecnie (PHPUnit 3.6.11); sam stanowi wyjątek. Korzystając z Twojego przykładu, jeśli $this->fail("Expected exception not thrown")zostanie wywołany, catchblok zostanie wyzwolony i $e->getMessage()zostanie wyświetlony komunikat „Oczekiwany wyjątek nie został zgłoszony” .
Ken
1
@ken prawdopodobnie masz rację. Wywołanie do failprawdopodobnie należy po bloku catch, a nie wewnątrz try.
Frank Farmer
1
Muszę przegłosować, ponieważ wezwanie do failnie powinno być w trybloku. To samo w sobie wyzwala catchblok, który daje fałszywe wyniki.
Twifty
6
Uważam, że powodem tego nie jest dobra sytuacja, ponieważ łapie wszystkie wyjątki catch(Exception $e). Ta metoda działa dla mnie całkiem dobrze, gdy próbuję wychwycić określone wyjątki:try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
spyle
23
Rozszerzenia assertException można użyć do potwierdzenia więcej niż jednego wyjątku podczas jednego wykonania testu.
Wstaw metodę do TestCase i użyj:
publicfunction testSomething(){
$test =function(){// some code that has to throw an exception};
$this->assertException( $test,'InvalidArgumentException',100,'expected message');}
Stworzyłem również cechę dla miłośników dobrego kodu ..
Z którego PHPUnit korzystasz? Korzystam z PHPUnit 4.7.5 i nie assertExceptionjest zdefiniowany. Nie mogę go również znaleźć w podręczniku PHPUnit.
fizyczna
2
asertExceptionMetoda nie jest częścią oryginalnego PHPUnit. Musisz odziedziczyć PHPUnit_Framework_TestCaseklasę i ręcznie dodać metodę podaną w poście powyżej . Twoje przypadki testowe odziedziczą tę odziedziczoną klasę.
Metoda PHPUnit expectExceptionjest bardzo niewygodna, ponieważ pozwala przetestować tylko jeden wyjątek na metodę testową.
Zrobiłem tę funkcję pomocnika, aby zapewnić, że jakaś funkcja zgłasza wyjątek:
/**
* 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');}
Dodaj go do klasy testowej i wywołaj w ten sposób:
Ponieważ chciałem więcej niż obecna expectExceptionimplementacja, postanowiłem wykorzystać tę cechę w moich testach. To tylko ~ 50 linii kodu .
Obsługuje wiele wyjątków na test
Obsługuje asercje wywoływane po zgłoszeniu wyjątku
Solidne i jasne przykłady użycia
Standard assert składnia
Obsługuje asercje dotyczące nie tylko wiadomości, kodu i klasy
Obsługuje odwrotne twierdzenie, assertNotThrows
Obsługuje Throwablebłędy PHP 7
Biblioteka
Opublikowałem AssertThrowscechę Github i packagist, aby można ją było zainstalować z kompozytorem.
Prosty przykład
Aby zilustrować ducha składni:
<?php
// Using simple callback
$this->assertThrows(MyException::class,[$obj,'doSomethingBad']);// Using anonymous function
$this->assertThrows(MyException::class,function()use($obj){
$obj->doSomethingBad();});
Dość schludny?
Pełny przykład użycia
Poniżej znajduje się pełniejszy przykład użycia:
<?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();});}}?>
Trochę ironiczne, że twój pakiet do testów jednostkowych nie zawiera testów jednostkowych w repozytorium.
domdambrogia
2
@domdambrogia dzięki @ jean-beguin ma teraz testy jednostkowe.
jchook
8
publicfunction testException(){try{
$this->methodThatThrowsException();
$this->fail("Expected Exception has not been raised.");}catch(Exception $ex){
$this->assertEquals($ex->getMessage(),"Exception message");}}
Podpis assertEquals()jest assertEquals(mixed $expected, mixed $actual...)odwrotny jak w twoim przykładzie, więc powinien być$this->assertEquals("Exception message", $ex->getMessage());
Roger Campanera,
7
Oto wszystkie możliwe wyjątki. Pamiętaj, że wszystkie są opcjonalne .
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!');}}
Jest to nieprawidłowe, ponieważ PHP zatrzymuje się przy pierwszym zgłoszonym wyjątku. PHPUnit sprawdza, czy zgłoszony wyjątek ma poprawny typ i mówi „test jest OK”, nawet nie wie o drugim wyjątku.
Finezja
3
/**
* @expectedException Exception
* @expectedExceptionMessage Amount has to be bigger then 0!
*/publicfunction testDepositNegative(){
$this->account->deposit(-7);}
Bądź bardzo ostrożny "/**", zauważ podwójne „*”. Pisanie tylko „**” (asterix) zawiedzie w twoim kodzie. Upewnij się także, że używasz ostatniej wersji phpUnit. W niektórych wcześniejszych wersjach phpunit @expectedException Wyjątek nie jest obsługiwany. Miałem 4.0 i nie działało to dla mnie, musiałem zaktualizować do wersji 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer, aby zaktualizować za pomocą kompozytora.
W przypadku PHPUnit 5.7.27 i PHP 5.6 oraz w celu przetestowania wielu wyjątków w jednym teście ważne było wymuszenie testowania wyjątków. Użycie samej obsługi wyjątków w celu potwierdzenia wystąpienia wyjątku spowoduje pominięcie testowania sytuacji, jeśli nie wystąpi wyjątek.
function yourfunction($a,$z){if($a<$z){thrownew<YOUR_EXCEPTION>;}}
tutaj jest test
classFunctionTestextends \PHPUnit_Framework_TestCase{publicfunction testException(){
$this->setExpectedException(<YOUR_EXCEPTION>::class);
yourfunction(1,2);//add vars that cause the exception }}
PhpUnit to niesamowita biblioteka, ale ten konkretny punkt jest nieco frustrujący. Dlatego możemy użyć biblioteki opensource turbotesting-php, która ma bardzo wygodną metodę asercji, która pomaga nam testować wyjątki. Można go znaleźć tutaj:
Aby go użyć, po prostu wykonalibyśmy następujące czynności:
AssertUtils::throwsException(function(){// Some code that must throw an exception here},'/expected error message/');
Jeśli kod, który wpisujemy w funkcji anonimowej, nie zgłasza wyjątku, wyjątek zostanie zgłoszony.
Jeśli kod, który wpisujemy w funkcji anonimowej, zgłasza wyjątek, ale jego komunikat nie pasuje do oczekiwanego wyrażenia regularnego, wyjątek również zostanie zgłoszony.
Odpowiedzi:
expectException () Dokumentacja PHPUnit
Artykuł autora PHPUnit zawiera szczegółowe objaśnienia dotyczące testowania najlepszych praktyk dotyczących wyjątków.
źródło
$this->setExpectedException('\My\Name\Space\MyCustomException');
setExpectedException
metoda jest przestarzała, zastąpiona tąexpectException
. :)expectException()
. Choć dla niektórych mogło to być oczywiste, dla mnie była to gotcha .Możesz także używać adnotacji docblock do czasu wydania PHPUnit 9:
W przypadku PHP 5.5+ (szczególnie z kodem przestrzeni nazw) wolę teraz używać
::class
źródło
IncorrectPasswordException
powinno wystarczyć - aby komunikat"Wrong password for [email protected]"
był pomocniczy. Dodaj do tego, że chcesz poświęcić jak najmniej czasu na pisanie testów, a zaczniesz widzieć, jak ważne stają się proste testy.Jeśli korzystasz z PHP 5.5+, możesz użyć
::class
rozdzielczości, aby uzyskać nazwę klasy za pomocąexpectException
/setExpectedException
. Zapewnia to kilka korzyści:string
tak, więc będzie działać z każdą wersją PHPUnit.Przykład:
Kompiluje PHP
w
bez PHPUnit jest mądrzejszy.
źródło
Poniższy kod przetestuje komunikat o wyjątku i kod wyjątku.
Ważne: zakończy się niepowodzeniem, jeśli nie zostanie zgłoszony oczekiwany wyjątek.
źródło
$this->fail()
nie powinien być używany w ten sposób, nie sądzę, przynajmniej obecnie (PHPUnit 3.6.11); sam stanowi wyjątek. Korzystając z Twojego przykładu, jeśli$this->fail("Expected exception not thrown")
zostanie wywołany,catch
blok zostanie wyzwolony i$e->getMessage()
zostanie wyświetlony komunikat „Oczekiwany wyjątek nie został zgłoszony” .fail
prawdopodobnie należy po bloku catch, a nie wewnątrz try.fail
nie powinno być wtry
bloku. To samo w sobie wyzwalacatch
blok, który daje fałszywe wyniki.catch(Exception $e)
. Ta metoda działa dla mnie całkiem dobrze, gdy próbuję wychwycić określone wyjątki:try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
Rozszerzenia assertException można użyć do potwierdzenia więcej niż jednego wyjątku podczas jednego wykonania testu.
Wstaw metodę do TestCase i użyj:
Stworzyłem również cechę dla miłośników dobrego kodu ..
źródło
assertException
jest zdefiniowany. Nie mogę go również znaleźć w podręczniku PHPUnit.asertException
Metoda nie jest częścią oryginalnego PHPUnit. Musisz odziedziczyćPHPUnit_Framework_TestCase
klasę i ręcznie dodać metodę podaną w poście powyżej . Twoje przypadki testowe odziedziczą tę odziedziczoną klasę.Alternatywnym sposobem może być:
Upewnij się, że twoja klasa testowa sięga
\PHPUnit_Framework_TestCase
.źródło
Metoda PHPUnit
expectException
jest bardzo niewygodna, ponieważ pozwala przetestować tylko jeden wyjątek na metodę testową.Zrobiłem tę funkcję pomocnika, aby zapewnić, że jakaś funkcja zgłasza wyjątek:
Dodaj go do klasy testowej i wywołaj w ten sposób:
źródło
Kompleksowe rozwiązanie
Obecne „ najlepsze praktyki ” PHPUnit dotyczące testowania wyjątków wydają się… brakiem połysku ( dokumenty ).
Ponieważ chciałem więcej niż obecna
expectException
implementacja, postanowiłem wykorzystać tę cechę w moich testach. To tylko ~ 50 linii kodu .assert
składniaassertNotThrows
Throwable
błędy PHP 7Biblioteka
Opublikowałem
AssertThrows
cechę Github i packagist, aby można ją było zainstalować z kompozytorem.Prosty przykład
Aby zilustrować ducha składni:
Dość schludny?
Pełny przykład użycia
Poniżej znajduje się pełniejszy przykład użycia:
źródło
źródło
assertEquals()
jestassertEquals(mixed $expected, mixed $actual...)
odwrotny jak w twoim przykładzie, więc powinien być$this->assertEquals("Exception message", $ex->getMessage());
Oto wszystkie możliwe wyjątki. Pamiętaj, że wszystkie są opcjonalne .
Dokumentacja znajduje się tutaj .
źródło
Bądź bardzo ostrożny
"/**"
, zauważ podwójne „*”. Pisanie tylko „**” (asterix) zawiedzie w twoim kodzie. Upewnij się także, że używasz ostatniej wersji phpUnit. W niektórych wcześniejszych wersjach phpunit @expectedException Wyjątek nie jest obsługiwany. Miałem 4.0 i nie działało to dla mnie, musiałem zaktualizować do wersji 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer, aby zaktualizować za pomocą kompozytora.źródło
W przypadku PHPUnit 5.7.27 i PHP 5.6 oraz w celu przetestowania wielu wyjątków w jednym teście ważne było wymuszenie testowania wyjątków. Użycie samej obsługi wyjątków w celu potwierdzenia wystąpienia wyjątku spowoduje pominięcie testowania sytuacji, jeśli nie wystąpi wyjątek.
źródło
tutaj jest test
źródło
PhpUnit to niesamowita biblioteka, ale ten konkretny punkt jest nieco frustrujący. Dlatego możemy użyć biblioteki opensource turbotesting-php, która ma bardzo wygodną metodę asercji, która pomaga nam testować wyjątki. Można go znaleźć tutaj:
https://github.com/edertone/TurboTesting/blob/master/TurboTesting-Php/src/main/php/utils/AssertUtils.php
Aby go użyć, po prostu wykonalibyśmy następujące czynności:
Jeśli kod, który wpisujemy w funkcji anonimowej, nie zgłasza wyjątku, wyjątek zostanie zgłoszony.
Jeśli kod, który wpisujemy w funkcji anonimowej, zgłasza wyjątek, ale jego komunikat nie pasuje do oczekiwanego wyrażenia regularnego, wyjątek również zostanie zgłoszony.
źródło