28

PHPUnitを使用して例外をテストする場合、テストに合格するためにすべてのステートメントまたはアサーションが例外をスローする必要があることを要求する最良の方法は何ですか?

私は基本的にこのようなことをしたいです:

public function testExceptions()
{

    $this->setExpectedException('Exception');

    foo(-1); //throws exception
    foo(1); //does not throw exception

}

//Test will fail because foo(1) did not throw an exception

私は次のことを思いついた。それは仕事をするが、かなり醜いIMOである。

public function testExceptions()
{

    try {
        foo(-1);
    } catch (Exception $e) {
        $hit = true;
    }

    if (!isset($hit))
        $this->fail('No exception thrown');

    unset($hit);

    try {
        foo(1);
    } catch (Exception $e) {
        $hit = true;
    }

    if (!isset($hit))
        $this->fail('No exception thrown');

    unset($hit);

}
4

5 に答える 5

30

これはユニットテストでは非常に一般的な状況だと思います。この場合に使用するアプローチは、phpunitdataProvidersを使用することです。すべてが期待どおりに機能し、テストコードがより明確で簡潔になります。

class MyTest extends PHPUnit\Framework\TestCase
{
    public function badValues(): array
    {
       return [
           [-1],
           [1]
       ];
    }


    /**
     * @dataProvider badValues
     * @expectedException Exception
     */
    public function testFoo($badValue): void
    {
        foo($badValue);
    }
}
于 2013-08-14T09:38:56.130 に答える
21

例外はプログラムフロー内のこのような大きなイベントであるため、1回のテストで複数のイベントをテストすることには問題があります。

最も簡単な方法は、単純に2つのテストに分割することです。1つ目は合格するために例外が必要で、2つ目は単純に実行され、失敗すると1つスローされます。必要に応じて、2番目に他のテストを追加することもできますが(戻り値を確認する場合もあります)、名前によると、それでも1つの重要なことだけが実行されることを確認したいと思います。

/**
 * @expectedException Exception
 */
public function testBadFooThrowsException()
{
    // optional, can also do it from the '@expectedException x'
    //$this->setExpectedException('Exception');
    foo(-1); //throws exception -- good.
}

public function testFooDoesNotThrowException()
{
    foo(1); //does not throw exception
}
于 2009-10-21T11:43:14.803 に答える
8

少しクリーンなコード(ただし、テストを分割することをお勧めします:

try {
    foo(-1);
    $this->fail('No exception thrown');
} catch (Exception $e) {}
于 2013-05-23T10:10:28.413 に答える
1

これは私には意味がありません。

1つのテストケースで複数の別々のものをテストしようとしていると思いますが、これは悪い習慣です。

予期された例外をスローするfoo()と、テストケースは成功し、bar()実行されません。

2番目のリストで作成したものよりもはるかに少ないコードである2つの別々のテストケースを作成するだけです。

bar()または、例外で失敗した後foo()、例外もスローするときに実行することが理にかなっている理由を説明します。

于 2009-10-20T11:09:56.883 に答える
1

@ dave1010の回答を拡張して、この問題を解決した方法を次に示します。これにより、これらすべての「アサーション」を1つのテスト内できちんと整理することができます。テストに失敗する変数の配列を定義し、それぞれをループして、例外が発生するかどうかを確認するだけです。いずれかが失敗した場合(例外がスローされない場合)、テストは失敗します。それ以外の場合、テストは合格します。

<?php

public function testSetInvalidVariableType()
{
    $invalid_vars = array(
        '',                 // Strings
        array(),            // Arrays
        true,               // Booleans
        1,                  // Integers
        new \StdClass       // Objects
    );

    foreach ($invalid_vars as $var) {
        try {
            $object->method($var);
            $this->fail('No exception thrown for variable type "' . gettype($var) . '".');
        } catch (\Exception $expected) {
        }
    }
}
于 2013-07-12T14:59:57.570 に答える