Asked  11 Months ago    Answers:  5   Viewed   7 times

I am getting numerous errors exactly like this one:

Zend_Session_Exception: Session must be started before any output has been sent to the browser; output started in /usr/local/zend/share/pear/PHPUnit/Util/Printer.php/173

When running my application's test suite. This is with PHPUnit 3.5.10 and PHP 5.3.5.

There is no mysterious, unexpected whitespace output that is causing this. I've determined that the "output being sent to the browser" is the actual output from the PHPUnit tests being executed. If I open up PHPUnit/Util/Printer.php and wrap the print $buffer line with if (strpos($buffer, 'PHPUnit 3.5.10 by Sebastian Bergmann') === false) (effectively stopping the first line of output from PHPUnit), then my first test succeeds (until the test case outputs a dot indicating that the test succeeded, then the next test fails because the dot was output).

Another developer on my team is able to run the full test suite successfully, so I know it's not a problem with the application code. It must be some configuration setting or problem with my local environment.

I've already checked php.ini to verify that output_buffering is turned on and implicit_flush is turned off, and they are.

I've also tried adding Zend_Session::$_unitTestEnabled = true; to my test bootstrap, but that didn't help (and shouldn't be necessary anyway because it works on another developer's machine and on our CI server without it).

Any suggestions besides ignoring the errors? I've never seen anything like this and am truly at a loss.

Thanks!

UPDATE:

To attempt to further isolate the problem, I took ZF and my application out of the equation by executing the following test script:

<?php

class SessionTest extends PHPUnit_Framework_TestCase
{
    public function testSession()
    {
        session_start();
        $this->assertTrue(true);
    }
}

The test fails:

1) SessionTest::testSession
session_start(): Cannot send session cookie - headers already sent by (output started at /home/mmsa/test.php:1)

However, the exact same test works on a friend's machine. Same version of PHP and PHPUnit.

 Answers

5

Run phpunit with the -stderr flag, (newer versions may use --stderr instead) e.g.

 phpunit -stderr mytest.php
 # or
 phpunit --stderr mytest.php

This directs phpunit's output to stderr, preventing it from interrupting HTTP header generation.

It's possible that the test works on your friend's machine because he has output buffering enabled (although I'm not sure if that's relevant in a CLI context).

Wednesday, July 28, 2021
 
tompave
 
2

By reducing the number of files included within code coverage, as well as increasing the overall memory limit in PHP, I was able to basically kill this error. The entire Zend Framework was being included within code coverage, which is very large.

Thursday, April 1, 2021
 
Exoon
 
5

The answer is "You don't". Unit testing should test each class in isolation, what you are trying to do there is not a unit test. As I said in my comment, you are breaking the Law of Demeter, which simply stated says

  • Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
  • Each unit should only talk to its friends; don't talk to strangers.
  • Only talk to your immediate friends.

You have tightly coupled classes there that need re-factoring. I have written the classes first here to illustrate the point, but I usually write the tests first.

Lets start with the end of the chain:-

class there3
{
    private $id

    public function setId($id)
    {
        $this->id = $id;
    }

    public function getId()
    {
        return $this->id;
    }

}

Now let's set up a unit test for it:-

class there3Test extends PHPUnit_Framework_TestCase
{
    public function testCanGetId()
    {
        $there3 = new there3();
        $there3->setId(3);
        $this->assertTrue($there3->getId() === 3);
    }
}

That class is now tested, so we don't need to test it again. Now let's look at the next one:-

class this2
{
    public $there3;

    //To facilitate unit testing we inject the dependency so we can mock it
    public function __construct(there3 $there3)
    {
        $this->there3 = $there3;
    }

    public function getId()
    {
        return $this->there3->getId();
    }

}

And now the unit test:-

class this2Test extends PHPUnit_Framework_TestCase
{
    public function testCanGetId()
    {
        $mockThere3 = $this->getMock('there3');
        $mockThere3->method('getId')
                   ->will($this->returnValue(3);

        $this2 = new this2($mockThere3);//We pass in the mock object instead of the real one
        $this->assertTrue($this2->getId() === 3);
    }
}

We'll do one last example to further illustrate my point:-

class this1
{
    private $this2;

    public function __construct(this2 $this2)//injecting again
    {
         $this->$this2 = $this2;
    }

    public function getId()
    {
        return $this->$this2->getId();
    }
}

And, again, the unit test:-

class this1Test extends PHPUnit_Framework_TestCase
{
    public function testCanGetId()
    {
        $mockThis2 = $this->getMock('this2');
        $mockThis2->method('getId')
                  ->will($this->returnValue(3);

        $this1 = new this1($mockThis2);//We pass in the mock object instead of the real one
        $this->assertTrue($this1->getId() === 3);
    }
}

Hopefully, you get the idea without me having to go through all the objects in your example.

What I have done is to de-couple the classes from each other. They only have knowledge of the object they depend on, they don't care how that object gets the information requested.

Now the call for id would look something like:-

public function getId()
{
    return $this->this1->getId();
}

Which will go up the chain until the id returned is there2::id. You never have to write something like $this->$this1->$this2->there3->id and you can unit test your classes properly.

For more information on unit testing see the PHPUnit manual.

Thursday, April 1, 2021
 
5

Following steps solved my problem.

sudo apt-get remove phpunit

sudo pear channel-discover pear.phpunit.de

sudo pear channel-discover pear.symfony.com

sudo pear channel-discover components.ez.no

sudo pear update-channels

sudo pear upgrade-all

sudo pear install --alldeps phpunit/PHPUnit

sudo pear install --force --alldeps phpunit/PHPUnit
Saturday, May 29, 2021
 
2

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');
}
Monday, June 21, 2021
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :