PHPUnit bir istisna atıldığını iddia?


337

assertTest edilen kodda bir istisna atılıp atılmadığını test edebilecek biri veya bunun gibi bir şey olup olmadığını bilen var mı ?


2
Bu cevaplara: ya bir test fonksiyonundaki çoklu iddialar hakkında, ve ben sadece bir atış istisnası olmasını bekliyorum? Onları ayırmalı ve bağımsız bir test fonksiyonuna koymalı mıyım?
Panwen Wang

Yanıtlar:


549
<?php
require_once 'PHPUnit/Framework.php';

class ExceptionTest extends PHPUnit_Framework_TestCase
{
    public function 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);
    }
}

waitException () PHPUnit belgeleri

PHPUnit yazar makalesi test istisnaları en iyi uygulamaları hakkında ayrıntılı açıklama sağlar.


8
$this->setExpectedException('\My\Name\Space\MyCustomException');
Ad

15
Atması beklenen kesin kod satırını belirleyememeniz, bir hata IMO'sudur. Ve aynı testte birden fazla istisnayı test edememe, birçok istisna için test etmeyi gerçekten zor bir iş haline getirir. Bu sorunları çözmeye çalışmak için gerçek bir iddia yazdım .
mindplay.dk

18
Bilginize: phpunit 5.2.0'dan itibaren setExpectedException yöntem kullanımdan kaldırılmıştır, yerine yenisi verilmiştir expectException. :)
hejdav

41
Dokümanlarda veya burada belirtilmeyen, ancak bir istisna atması beklenen kodun daha sonra çağrılması gerekir expectException(). O bazı bariz olmuş olsa da, bu bir oldu yakaladım benim için.
Jason McCreary

7
Dokümandan belli değil, ancak işlevinizden sonra bir istisna atan hiçbir kod yürütülmeyecek. Dolayısıyla, aynı test durumunda birden fazla istisnayı test etmek istiyorsanız yapamazsınız.
laurent

122

PHPUnit 9 yayınlanana kadar docblock ek açıklaması da kullanabilirsiniz :

class ExceptionTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException InvalidArgumentException
     */
    public function testException()
    {
        ...
    }
}

PHP 5.5+ için (özellikle ad boşluklu kodda), şimdi kullanmayı tercih ediyorum ::class


3
IMO, bu tercih edilen yöntemdir.
Mike Purcell

12
@LeviMorrison - IMHO istisna mesajı , günlük mesajlarına benzer şekilde test edilmemelidir. Her ikisi de manuel adli tıp yapılırken yabancı, yararlı bilgiler olarak kabul edilir . Test edilecek kilit nokta istisna türüdür . Bunun ötesinde herhangi bir şey uygulamaya çok sıkı bağlanıyor. IncorrectPasswordExceptionyeterli olmalıdır - mesajın eşit "Wrong password for bob@me.com"olması yardımcı olur. Buna testler yazmak için mümkün olduğunca az zaman harcamak istediğinizi de ekleyin ve basit testlerin ne kadar önemli olduğunu görmeye başlayın.
David Harkness

5
@DavidHarkness Birisinin bunu gündeme getireceğini düşündüm. Benzer şekilde, genel olarak mesajların test edilmesinin çok katı ve sıkı olduğunu kabul ediyorum. Bununla birlikte , bir şartnamenin uygulanması gibi bazı durumlarda istenen (kasıtlı olarak vurgulanan) olabilecek sıkılık ve sıkı bağlanmadır .
Levi Morrison

1
Ne beklediğini anlamak için bir doc bloğunda izlemezdim, ama gerçek test koduna bakarım (ne tür bir test olursa olsun). Diğer tüm testler için standart budur; İstisnaların (tanrım) bu sözleşmenin istisnası olması için geçerli nedenler görmüyorum .
Kamafeather

3
Kodun birden çok bölümüne aynı özel durum türünü atan bir yöntemi test etmedikçe, tek fark iletide iletilen hata kimliği olan "iletiyi sınama" kuralı geçerli görünür. Sisteminiz, İstisna mesajına (İstisna türüne değil) dayalı olarak kullanıcıya bir mesaj görüntüleyebilir. Bu durumda, kullanıcının hangi mesajı gördüğü önemli değildir, bu nedenle hata mesajını test etmeniz gerekir.
Vanja

34

PHP 5.5+ üzerinde çalışıyorsanız, / ile sınıfın adını almak için ::classçözünürlük kullanabilirsiniz . Bu çeşitli faydalar sağlar:expectExceptionsetExpectedException

  • Ad, (varsa) ad alanı ile tam olarak nitelenir.
  • stringPHPUnit'in herhangi bir sürümü ile çalışacak şekilde çözülür .
  • IDE'nizde kod tamamlama elde edersiniz.
  • Sınıf adını yanlış yazarsanız PHP derleyicisi hata verir.

Misal:

namespace \My\Cool\Package;

class AuthTest extends \PHPUnit_Framework_TestCase
{
    public function testLoginFailsForWrongPassword()
    {
        $this->expectException(WrongPasswordException::class);
        Auth::login('Bob', 'wrong');
    }
}

PHP derlemeleri

WrongPasswordException::class

içine

"\My\Cool\Package\WrongPasswordException"

PHPUnit bilgelik olmadan.

Not : PHPUnit 5.2 expectException yerine geçmiştirsetExpectedException .


32

Aşağıdaki kod, istisna mesajını ve istisna kodunu test edecektir.

Önemli: Beklenen istisna da atılmazsa başarısız olur.

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());
}

6
$this->fail()en azından şu anda (PHPUnit 3.6.11) sanmıyorum, bu şekilde kullanılmak üzere tasarlanmamıştır; bir istisna görevi görür. Örneğiniz kullanılırsa $this->fail("Expected exception not thrown"), çağrılırsa, catchblok tetiklenir ve "Beklenen istisna atılmaz"$e->getMessage() dır .
ken

1
@ken muhtemelen haklısın. failMuhtemelen arama denemenin içinde değil, catch bloğunun ardına aittir .
Frank Farmer

1
Aşağı inmek zorundayım çünkü çağrı blokta failolmamalı try. Kendi başına catchbloğu yanlış sonuçlar üretmeye iter.
Twifty

6
Bunun iyi çalışmamasının nedeninin bazı istisnaları yakalaması olduğuna inanıyorum catch(Exception $e). Belirli İstisnaları yakalamaya çalıştığımda bu yöntem benim için oldukça iyi çalışıyor:try { throw new MySpecificException; $this->fail('MySpecificException not thrown'); } catch(MySpecificException $e){}
spyle

23

Bir sınama yürütme sırasında birden fazla özel durum bildirmek için assertException uzantısını kullanabilirsiniz .

TestCase'inize yöntem ekleyin ve şunu kullanın:

public function testSomething()
{
    $test = function() {
        // some code that has to throw an exception
    };
    $this->assertException( $test, 'InvalidArgumentException', 100, 'expected message' );
}

Ayrıca güzel kod sevenler için bir özellik yaptım ..


Hangi PHPUnit'i kullanıyorsunuz? PHPUnit 4.7.5 kullanıyorum ve assertExceptiontanımlı değil. Ayrıca PHPUnit kılavuzunda bulamıyorum.
physicalattraction

2
asertExceptionYöntem orijinal PHPUnit bir parçası değil. PHPUnit_Framework_TestCaseSınıfı miras almalı ve yukarıdaki gönderiye bağlı yöntemi manuel olarak eklemelisiniz . Test durumlarınız daha sonra bu kalıtsal sınıfı devralır.
hejdav

18

Alternatif bir yol aşağıdakiler olabilir:

$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Expected Exception Message');

Lütfen test sınıfınızın genişlediğinden emin olun \PHPUnit_Framework_TestCase.


Kesinlikle bu sözdizimindeki en şeker
AndrewMcLagan

13

PHPUnit expectExceptionyöntemi çok elverişsizdir çünkü bir test yöntemi için yalnızca bir istisnayı test etmeyi sağlar.

Bazı fonksiyonlar bir istisna atar iddia bu yardımcı işlevi yaptım:

/**
 * 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
 */
protected function 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');
}

Test sınıfınıza ekleyin ve şu şekilde arayın:

public function testSomething() {
    $this->assertException(\PDOException::class, function() {
        new \PDO('bad:param');
    });
    $this->assertException(\PDOException::class, function() {
        new \PDO('foo:bar');
    });
}

Tüm cevaplar kesinlikle en iyi çözüm! Bir özelliğe atın ve paketleyin!
domdambrogia

11

Kapsamlı Çözüm

PHPUnit'in istisna testi için mevcut " en iyi uygulamaları " görünmektedir .. cansız ( dokümanlar ).

Ben yana fazla istedi cari fazla expectExceptionuygulanması, benim test durumları kullanılmak üzere bir özellik yaptı. Sadece ~ 50 satır kod .

  • Test başına birden fazla istisnayı destekler
  • İstisna atıldıktan sonra çağrılan iddiaları destekler
  • Sağlam ve net kullanım örnekleri
  • Standart assertsözdizimi
  • Mesaj, kod ve sınıftan daha fazlası için iddiaları destekler
  • Ters iddiayı destekler, assertNotThrows
  • PHP 7 Throwablehatalarını destekler

Kütüphane

Besteci ile yüklenebilmesi için AssertThrowsözelliği Github ve paketci olarak yayınladım .

Basit Örnek

Sadece sözdiziminin arkasındaki ruhu göstermek için:

<?php

// Using simple callback
$this->assertThrows(MyException::class, [$obj, 'doSomethingBad']);

// Using anonymous function
$this->assertThrows(MyException::class, function() use ($obj) {
    $obj->doSomethingBad();
});

Oldukça temiz mi?


Tam Kullanım Örneği

Daha kapsamlı bir kullanım örneği için lütfen aşağıya bakın:

<?php

declare(strict_types=1);

use Jchook\AssertThrows\AssertThrows;
use PHPUnit\Framework\TestCase;

// These are just for illustration
use MyNamespace\MyException;
use MyNamespace\MyObject;

final class MyTest extends TestCase
{
    use AssertThrows; // <--- adds the assertThrows method

    public function testMyObject()
    {
        $obj = new MyObject();

        // 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();
        });
    }
}

?>

4
Birim testi paketinizin repoda birim testleri içermemesi biraz ironik.
domdambrogia

2
@domdambrogia @ jean-beguin sayesinde artık birim testleri var.
jchook

8
public function testException() {
    try {
        $this->methodThatThrowsException();
        $this->fail("Expected Exception has not been raised.");
    } catch (Exception $ex) {
        $this->assertEquals($ex->getMessage(), "Exception message");
    }

}

İmzası, örneğinizde assertEquals()olduğu assertEquals(mixed $expected, mixed $actual...)gibi ters, öyle olmalı$this->assertEquals("Exception message", $ex->getMessage());
Roger Campanera

7

İşte yapabileceğiniz tüm istisna iddiaları. Hepsinin isteğe bağlı olduğunu unutmayın .

class ExceptionTest extends PHPUnit_Framework_TestCase
{
    public function testException()
    {
        // make your exception assertions
        $this->expectException(InvalidArgumentException::class);
        // if you use namespaces:
        // $this->expectException('\Namespace\MyExceptio‌​n');
        $this->expectExceptionMessage('message');
        $this->expectExceptionMessageRegExp('/essage$/');
        $this->expectExceptionCode(123);
        // code that throws an exception
        throw new InvalidArgumentException('message', 123);
   }

   public function testAnotherException()
   {
        // repeat as needed
        $this->expectException(Exception::class);
        throw new Exception('Oh no!');
    }
}

Belgeleri burada bulabilirsiniz .


Bu yanlıştır çünkü PHP ilk atılan istisnada durur. PHPUnit atılan istisnanın doğru tipte olup olmadığını kontrol eder ve “test tamamdır” der, ikinci istisna hakkında bile bilgi sahibi değildir.
Finesse

3
/**
 * @expectedException Exception
 * @expectedExceptionMessage Amount has to be bigger then 0!
 */
public function testDepositNegative()
{
    $this->account->deposit(-7);
}

Çok dikkatli olun "/**", çift "*" dikkat edin. Yalnızca "**" (asterix) yazdığınızda kodunuz başarısız olur. Ayrıca phpUnit'in son sürümünü kullandığınızdan emin olun. Daha önceki bazı phpunit sürümlerinde @expectedException İstisna desteklenmez. 4.0 vardı ve benim için çalışmadı , besteci ile güncellemek için 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer güncellemek zorunda kaldı .


0

PHPUnit 5.7.27 ve PHP 5.6 için ve bir testte birden fazla istisnayı test etmek için istisna testini zorlamak önemliydi. İstisna örneğini iddia etmek için yalnızca istisna işlemeyi kullanmak, istisna olmazsa durumu test etmeyi atlayacaktır.

public function testSomeFunction() {

    $e=null;
    $targetClassObj= new TargetClass();
    try {
        $targetClassObj->doSomething();
    } catch ( \Exception $e ) {
    }
    $this->assertInstanceOf(\Exception::class,$e);
    $this->assertEquals('Some message',$e->getMessage());

    $e=null;
    try {
        $targetClassObj->doSomethingElse();
    } catch ( Exception $e ) {
    }
    $this->assertInstanceOf(\Exception::class,$e);
    $this->assertEquals('Another message',$e->getMessage());

}

0
function yourfunction($a,$z){
   if($a<$z){ throw new <YOUR_EXCEPTION>; }
}

test burada

class FunctionTest extends \PHPUnit_Framework_TestCase{

   public function testException(){

      $this->setExpectedException(<YOUR_EXCEPTION>::class);
      yourfunction(1,2);//add vars that cause the exception 

   }

}

0

PhpUnit inanılmaz bir kütüphane, ama bu özel nokta biraz sinir bozucu. Bu nedenle, istisnaları test etmemize yardımcı olmak için çok uygun bir iddia yöntemine sahip turbotesting-php açık kaynak kütüphanesini kullanabiliriz. Burada bulunur:

https://github.com/edertone/TurboTesting/blob/master/TurboTesting-Php/src/main/php/utils/AssertUtils.php

Ve bunu kullanmak için aşağıdakileri basitçe yaparız:

AssertUtils::throwsException(function(){

    // Some code that must throw an exception here

}, '/expected error message/');

Anonim işlevin içine yazdığımız kod bir istisna atmazsa, bir istisna atılır.

Anonim işlevin içine yazdığımız kod bir istisna atar, ancak iletisi beklenen regexp ile eşleşmezse, bir istisna da atılır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.