web-dev-qa-db-ja.com

テストPHP PHPUnitを使用したヘッダー

PHPunitを使用して、いくつかのカスタムヘッダーを出力するクラスをテストしようとしています。

問題は、私のマシンでは次のことです:

<?php

class HeadersTest extends PHPUnit_Framework_TestCase {

    public function testHeaders()
    {
        ob_start();

        header('Location: foo');
        $headers_list = headers_list();
        header_remove();

        ob_clean();

        $this->assertContains('Location: foo', $headers_list);
    }
}

またはこれさえ:

<?php

class HeadersTest extends PHPUnit_Framework_TestCase {

    public function testHeaders()
    {
        ob_start();

        header('Location: foo');
        header_remove();

        ob_clean();
    }
}

このエラーを返します:

name@Host [~/test]# phpunit --verbose HeadersTest.php 
PHPUnit 3.6.10 by Sebastian Bergmann.

E

Time: 0 seconds, Memory: 2.25Mb

There was 1 error:

1) HeadersTest::testHeaders
Cannot modify header information - headers already sent by (output started at /usr/local/lib/php/PHPUnit/Util/Printer.php:173)

/test/HeadersTest.php:9

FAILURES!
Tests: 1, Assertions: 0, Errors: 1.

これは、他のファイルが含まれておらず、PHPタグの先頭の前に他の文字がない場合でも、テストの実行前に端末に出力する何かがあるように見えます。これを引き起こしているPHPunit内の何か?

問題は何ですか?

82
titel

問題は、PHPUnitがヘッダーを画面に出力することであり、その時点でヘッダーを追加することはできません。

回避策は、隔離されたプロセスでテストを実行することです。ここに例があります

<?php

class FooTest extends PHPUnit_Framework_TestCase
{
    /**
     * @runInSeparateProcess
     */
    public function testBar()
    {
        header('Location : http://foo.com');
    }
}

これにより、次の結果が得られます。

$ phpunit FooTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

.

Time: 1 second, Memory: 9.00Mb

OK (1 test, 0 assertions)

キーは@runInSeparateProcessアノテーションです。

PHPUnit〜4.1などを使用しているときにエラーが発生する場合:

PHP Fatal error:  Uncaught Error: Class 'PHPUnit_Util_Configuration' not found in -:378
Stack trace:
#0 {main}
  thrown in - on line 378

Fatal error: Uncaught Error: Class 'PHPUnit_Util_Configuration' not found in - on line 378

Error: Class 'PHPUnit_Util_Configuration' not found in - on line 378

Call Stack:
    0.0013     582512   1. {main}() -:0

これをbootstrapファイルに追加して修正してください:

<?php
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
    define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/path/to/composer/vendors/dir/autoload.php');
}
111
SamHennessy

テストを別のプロセスで実行することで問題は解決しますが、大規模なテストスイートを実行すると顕著なオーバーヘッドが発生します。

私の修正は、phpunitの出力を次のようにstderrに送ることでした:

phpunit --stderr <options>

これで問題は解決するはずです。また、ラッパー関数を作成してコード内のすべての出現箇所を置き換える必要もありません。

99
Jon Cairns

余談ですが、私にとってはheaders_list()は0個の要素を返し続けました。 @ titel の質問に対するコメントに気づき、ここで特に言及する価値があると考えました。

他にも興味がある人がいるなら、これをカバーしたかっただけです。 headers_list()はPHPunit(PHP CLI)を使用)の実行中は機能しませんが、xdebug_get_headers()は代わりに機能します。

HTH

8
Melle

tested/includedファイル内で_$_SESSION_を使用するために、より根本的な解決策がありました。 PHPUnit../ PHPUnit/Utils/Printer.phpのファイルの1つを編集して、command "の前に"session_start();"を追加しましたprint $ buffer "

それは私にとって魅力的でした。しかし、私はこれまでのところ、「不愉快な」ユーザーのソリューションが何よりも優れていると思います。

3
Sergio Abreu

コメントですでに述べたように、XML構成ファイルでprocessIsolationを次のように定義する方が良い解決策だと思います

     <?xml version="1.0" encoding="UTF-8"?>
     <phpunit
        processIsolation            = "true"
        // ... 
     >
     </phpunit>

このように、同僚を苛立たせる可能性がある--stderrオプションを渡す必要はありません。

0
PepeNietnagel

@runInSeparateProcessの代替ソリューションは、PHPUnitの実行時に--process-isolationオプションを指定することです。

name@Host [~/test]# phpunit --process-isolation HeadersTest.php

これは、phpunit.xmlでprocessIsolation = "true"オプションを設定することに似ています。

このソリューションには--stderrオプションを指定するのと同様の利点/欠点がありますが、私の場合はうまくいきませんでした。各テストを個別のPHPプロセスで実行するためにパフォーマンスが低下する可能性がありますが、基本的にコードを変更する必要はありません。

0
AlexB