Test PHP headers with PHPUnit

PhpUnit TestingPhpunitOutput Buffering

Php Problem Overview


I'm trying to use PHPunit to test a class that outputs some custom headers.

The problem is that on my machine this:

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

or even this:

<?php

class HeadersTest extends PHPUnit_Framework_TestCase {

	public function testHeaders()
    {
		ob_start();

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

		ob_clean();
    }
}

return this error:

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.

This looks as if there is something else outputting to the terminal before the test runs even though there is no other file included and there is no other character before the beginning of the PHP tag. Could it be something inside PHPunit that is causing this?

What could the issue be?

Php Solutions


Solution 1 - Php

The issue is that PHPUnit will print a header to the screen and at that point you can't add more headers.

The work around is to run the test in an isolated process. Here is an example

<?php

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

This will result in:

$ phpunit FooTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

.

Time: 1 second, Memory: 9.00Mb

OK (1 test, 0 assertions)

The key is the @runInSeparateProcess annotation.

If you are using PHPUnit ~4.1 or something and get the error:

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

Try add this to your bootstrap file to fix it:

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

Solution 2 - Php

Although running the test in a separate process does fix the problem, there's a noticeable overhead when running a large suite of tests.

My fix was to direct phpunit's output to stderr, like so:

phpunit --stderr <options>

That should fix the problem, and it also means that you don't have to create a wrapper function and replace all occurrences in your code.

Solution 3 - Php

As an aside: For me headers_list() kept returning 0 elements. I noticed @titel's comment on the question and figured it deserves special mention here:

> Just wanted to cover this if there are some other people interested > in this as well. headers_list() doesn't work while running PHPunit > (which uses PHP CLI) but xdebug_get_headers() works instead.

HTH

Solution 4 - Php

As already mentioned in a comment, I think it's a better solution to define processIsolation in the XML config file like

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

Like this, you don't have to pass the --stderr option, which might irritate your co-workers.

Solution 5 - Php

I had a more radical solution, in order to use $_SESSION inside my tested/included files. I edited one of the PHPUnit files at ../PHPUnit/Utils/Printer.php to have a "session_start();" before the command "print $buffer".

It worked for me like a charm. But I think "joonty" user's solution is the best of all up to now.

Solution 6 - Php

An alternative solution to @runInSeparateProcess is to specify the --process-isolation option when running PHPUnit:

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

That is analogous to set the processIsolation="true" option in phpunit.xml.

This solution has similar advantages/disadvantages to specifying the --stderr option, which however did not work in my case. Basically no code changes are necessary, even though there may be a performance hit due to running each test in a separate PHP process.

Solution 7 - Php

Use --stderr parameter for getting headers from PHPUnit after your tests.

phpunit --stderr

Solution 8 - Php

if you're using Laravel and you're adding some headers in route file

then you need to surround with headers_sent to ignore during tests

this is example:

if (!headers_sent()) {
    header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
    header('Access-Control-Allow-Headers: Origin, Access-Control-Allow-Origin,  Content-Type, X-Authorization, Authorization, Accept,charset,boundary,Content-Length');
    header('Access-Control-Allow-Origin: *');
}

then try unit test again it will pass..

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontitelView Question on Stackoverflow
Solution 1 - PhpSamHennessyView Answer on Stackoverflow
Solution 2 - PhpJon CairnsView Answer on Stackoverflow
Solution 3 - PhpMelleView Answer on Stackoverflow
Solution 4 - PhpPepeNietnagelView Answer on Stackoverflow
Solution 5 - PhpSergio AbreuView Answer on Stackoverflow
Solution 6 - PhpAlexBView Answer on Stackoverflow
Solution 7 - PhpNSukonnyView Answer on Stackoverflow
Solution 8 - PhpAlaa MoneamView Answer on Stackoverflow