Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing with items that need to send headers

I'm currently working with PHPUnit to try and develop tests alongside what I'm writing, however, I'm currently working on writing the Session Manager, and am having issues doing so...

The constructor for the Session handling class is

private function __construct()
{
    if (!headers_sent())
    {
        session_start();
        self::$session_id = session_id();
    }
}

However, as PHPUnit sends out text before it starts the testing, any testing on this Object returns a failed test, as the HTTP "Headers" have been sent...

like image 558
Mez Avatar asked Oct 10 '08 06:10

Mez


3 Answers

Well, your session manager is basically broken by design. To be able to test something, it must be possible to isolate it from side effects. Unfortunately, PHP is designed in such a way, that it encourages liberal use of global state (echo, header, exit, session_start etc. etc.).

The best thing you can do, is to isolate the side-effects in a component, that can be swapped at runtime. That way, your tests can use mocked objects, while the live code uses adapters, that have real side-effects. You'll find that this doesn't play well with singletons, which I presume you're using. So you'll have to use some other mechanism for getting shared objects distributed to your code. You can start with a static registry, but there are even better solutions if you don't mind a bit of learning.

If you can't do that, you always have the option of writing integration-tests. Eg. use the PHPUnit's equivalent of WebTestCase.

like image 111
troelskn Avatar answered Nov 07 '22 06:11

troelskn


Create a bootstrap file for phpunit, which calls:

session_start();

Then start phpunit like this:

phpunit --bootstrap pathToBootstrap.php --anotherSwitch /your/test/path/

The bootstrap file gets called before everything else, so the header hasn't been sent and everything should work fine.

like image 20
Dominik Avatar answered Nov 07 '22 07:11

Dominik


phpUnit prints output as the tests run thus causing headers_sent() to return true even in your first test.

To overcome this issue for an entire test suite you simply need to use ob_start() in your setup script.

For example, say you have a file named AllTests.php that is the first thing loaded by phpUnit. That script might look like the following:

<?php

ob_start();

require_once 'YourFramework/AllTests.php';

class AllTests {
    public static function suite() {
        $suite = new PHPUnit_Framework_TestSuite('YourFramework');
        $suite->addTest(YourFramework_AllTests::suite());
        return $suite;
    }
}
like image 19
Michael Avatar answered Nov 07 '22 07:11

Michael