13

この質問は、PHPUnitの使用に固有のものです。

PHPUnitは、phpエラーを例外に自動的に変換します。phpエラー(組み込みエラーまたはtrigger_errorを介してユーザーが生成したエラー)をトリガーするメソッドの戻り値をテストする方法はありますか?

テストするコードの例:

function load_file ($file)
{
    if (! file_exists($file)) {
        trigger_error("file {$file} does not exist", E_USER_WARNING);
        return false;
    }
    return file_get_contents($file);
}

これは私が書きたいテストのタイプです:

public function testLoadFile ()
{
    $this->assertFalse(load_file('/some/non-existent/file'));
}

私が抱えている問題は、トリガーされたエラーが原因でユニットテストが失敗することです(そうあるべきです)。しかし、それをキャッチしようとしたり、予期される例外を設定したりすると、エラーがトリガーされた後に実行されないコードが実行されないため、メソッドの戻り値をテストする方法がありません。

この例は機能しません:

public function testLoadFile ()
{
    $this->setExpectedException('Exception');
    $result = load_file('/some/non-existent/file');

    // code after this point never gets executed

    $this->assertFalse($result);
}

これをどのように達成できるかについてのアイデアはありますか?

4

5 に答える 5

22

1つの単体テスト内でこれを行う方法はありません。戻り値のテストと通知を2つの異なるテストに分割すると可能です。

PHPUnitのエラーハンドラーは、PHPエラーをキャッチして通知し、それらを例外に変換します。これは、定義上、プログラムの実行を停止します。テストしている関数が返されることはありません。ただし、実行時であっても、エラーから例外への変換を一時的に無効にすることができます。

これは例を使用するとおそらく簡単なので、2つのテストは次のようになります。

public function testLoadFileTriggersErrorWhenFileNotFound()
{
    $this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is
    $result = load_file('/some/non-existent/file');

}

public function testLoadFileRetunsFalseWhenFileNotFound()
{
    PHPUnit_Framework_Error_Warning::$enabled = FALSE;
    $result = load_file('/some/non-existent/file');

    $this->assertFalse($result);
}

これには、テストをより明確で、よりクリーンで、自己文書化するという追加のボーナスもあります。

Re:コメント: それは素晴らしい質問です。いくつかのテストを実行するまで、私にはわかりませんでした。少なくともPHPUnit3.3.17(現在の安定版リリース)では、デフォルト/元の値を復元しないように見えます。

だから、私は実際に上記を次のように修正します:

public function testLoadFileRetunsFalseWhenFileNotFound()
{
    $warningEnabledOrig = PHPUnit_Framework_Error_Warning::$enabled;
    PHPUnit_Framework_Error_Warning::$enabled = false;

    $result = load_file('/some/non-existent/file');

    $this->assertFalse($result);

    PHPUnit_Framework_Error_Warning::$enabled = $warningEnabledOrig;
}

Re:2番目のコメント:

それは完全に真実ではありません。PHPUnitのエラーハンドラーを見ていますが、次のように機能します。

  • の場合は、例外クラスとしてE_WARNING使用します。PHPUnit_Framework_Error_Warning
  • E_NOTICEまたはE_STRICTエラーの場合は、PHPUnit_Framework_Error_Notice
  • PHPUnit_Framework_Errorそれ以外の場合は、例外クラスとして使用します。

したがって、はい、エラーはE_USER_*PHPUnitの*_Warningまたは*_Noticeクラスに変換されませんが、それでも一般的なPHPUnit_Framework_Error例外に変換されます。

さらなる考察

関数の使用方法によって異なりますが、私であれば、エラーをトリガーするのではなく、実際の例外をスローするように切り替えると思います。はい、これによりメソッドのロジックフローが変更され、メソッドを使用するコードが変更されます...現在、ファイルを読み取れないときに実行が停止することはありません。しかし、要求されたファイルが存在しないことが本当に例外的な動作であるかどうかを判断するのはあなた次第です。例外は、アプリケーションフローの処理、テスト、および作業が簡単であるため、エラー/警告/通知よりもはるかに多く使用する傾向があります。私は通常、減価償却されたメソッド呼び出しなどの通知を予約します。

于 2009-08-04T13:56:43.010 に答える
9

構成ファイルを使用phpunit.xmlして、通知/警告/エラーから例外への変換を無効にします。詳細については、マニュアルをご覧ください。基本的には次のようなものです。

<phpunit convertErrorsToExceptions="false"
         convertNoticesToExceptions="false"
         convertWarningsToExceptions="false">
</phpunit>
于 2009-08-04T09:40:51.957 に答える
3

Exception一般的な" "を期待する代わりに、 " "を期待するのはどうPHPUnit_Framework_Errorですか?

このような何かがするかもしれません:

/**
 * @expectedException PHPUnit_Framework_Error
 */
public function testFailingInclude()
{
    include 'not_existing_file.php';
}

これは、私が思うに、次のように書くこともできます:

public function testLoadFile ()
{
    $this->setExpectedException('PHPUnit_Framework_Error');
    $result = load_file('/some/non-existent/file');

    // code after this point never gets executed

    $this->assertFalse($result);
}

詳細については、「PHPエラーのテスト」
を参照してください。特に、(引用符で囲んで):

PHPUnit_Framework_Error_NoticePHPUnit_Framework_Error_WarningはそれぞれPHPの通知と警告を表します。


システムにある/usr/share/php/PHPUnit/TextUI/TestRunner.phpファイルを見ると、次のようになっています(198行目以降)

if (!$arguments['convertNoticesToExceptions']) {
    PHPUnit_Framework_Error_Notice::$enabled = FALSE;
}

if (!$arguments['convertWarningsToExceptions']) {
    PHPUnit_Framework_Error_Warning::$enabled = FALSE;
}

それで、多分あなたはその振る舞いを活性化するためにある種のパラメータを渡さなければならないでしょう?しかし、デフォルトで有効になっているようです...

于 2009-08-04T05:21:35.190 に答える
2

実際には、戻り値とスローされた例外(この場合はPHPUnitによって変換されたエラー)の両方をテストする方法があります。

次のことを行う必要があります。

public function testLoadFileTriggersErrorWhenFileNotFound()
{
    $this->assertFalse(@load_file('/some/non-existent/file'));

    $this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is
    load_file('/some/non-existent/file');
}

戻り値をテストするには、関数呼び出し(@関数名の前)でエラー抑制演算子を使用する必要があることに注意してください。このように、例外はスローされず、実行が続行されます。次に、エラーをテストするために、通常どおりに予期される例外を設定する必要があります。

できないことは、単体テスト内で複数の例外をテストすることです。

于 2013-12-03T23:28:25.403 に答える
0

この答えはパーティーに少し遅れていますが、とにかく:

Netsilik / BaseTestCase (MITライセンス)を使用して、トリガーされた通知/警告を無視したり例外に変換したりすることなく、それらを直接テストできます。通知/警告は例外に変換されないため、実行は停止されません。

composer require netsilik/base-test-case


のテストE_USER_NOTICE

<?php
namespace Tests;

class MyTestCase extends \Netsilik\Testing\BaseTestCase
{        
    public function test_whenNoticeTriggered_weCanTestForIt()
    {
        $foo = new Foo();
        $foo->bar();

        self::assertErrorTriggered(E_USER_NOTICE, 'The notice message');
    }
}

これが将来誰かに役立つことを願っています。

于 2019-08-14T09:24:48.620 に答える