GordonM
GordonM

Reputation: 31730

PHPUnit: Disable autoloader for autoload testing problems

I have a class in my project that manages autoloading. Each new instance of the class adds its autoload function to the SPL autoload stack, and unsetting an instance removes its own instance from the stack. The class also exports a register () and unregister () method that lets you remove it from the autoload stack on a temporary basis.

I'm attempting to write a unit test for the autoloader and have run into a few problems. I include the autoloader in my PHPUnit bootstrap script so other classes under test can autoload as they would under normal use. I want to disable this behaviour for the duration of the autoloader unit test, as while I can unit test without disabling the normal autoloader, I couldn't be sure if my unit test was passing or failing due to the instance of the autoloader that's in the bootstrap or the instance I'm testing.

I tried doing the following in my bootstrap file:

$unitTestAutoloader = new gordian\reefknot\autoload\Autoload ();

and then implementing the following code in my unit test:

namespace gordian\reefknot\autoload;

use gordian\exampleclasses;

/**
 * Test class for Autoload.
 * Generated by PHPUnit on 2011-12-17 at 18:10:33.
 */
class AutoloadTest extends \PHPUnit_Framework_TestCase
{
/**
 * @var gordian\reefknot\autoload\Autoload
 */
protected $object;

public function __construct ()
{
    // Disable the unit test autoloader for the duration of the following test
    global $unitTestAutoloader;
    $unitTestAutoloader -> unregister ();
}

public function __destruct ()
{
    // Restore normal autoloading when the test is done
    global $unitTestAutoloader;
    $unitTestAutoloader -> register ();
}

/**
 * Sets up the fixture, for example, opens a network connection.
 * This method is called before a test is executed.
 */
protected function setUp ()
{
    $this -> object = new Autoload ('\\', 'gordian\exampleclasses', __DIR__ . '/exampleclasses');
}

    // Unit tests go here
}

I thought this would work, but unfortunately it just throws an error when I run all unit tests instead, apparently because the autloader isn't functioning for any of the other unit tests.

My suspicion is PHPUnit initializes all the unit test classes before running all tests, and my autoloader test class is preventing the other test classes from autoloading the classes they are meant to test. Is this correct?

Is there some way I can work around this problem? Would I be able to disable the default autoloader some way other than doing it in the test constructor? Any advice in this case would be appreciated.

Upvotes: 2

Views: 2026

Answers (3)

David Harkness
David Harkness

Reputation: 36532

The reason the autoloader doesn't work for the other tests is that PHPUnit instantiates all test case instances (one per test method and data provider method) before running any tests and holds on to them forever. Your destructor runs only after all tests have been run. As you found, you must move these to setUp() and tearDown(). I assumed that's what they were when I read your answer initially. :)

Also, the PHPUnit test case constructor accept parameters that must be passed in by your overridden constructor--at least the $name argument.

Upvotes: 1

David Harkness
David Harkness

Reputation: 36532

For my autoloading unit tests, I created a few classes just for these tests that live in the test source tree. Since the test directory isn't registered with the real autoloading, it cannot find them.

If you want to make your tests bullet-proof, verify that the class cannot be loaded normally using class_exists().

function testAutoloadLoadsClass() {
    self::assertFalse(class_exists('My_Test_Class'), 'Class should not load normally');
    self::assertTrue($this->fixture->autoload('My_Test_Class'), 'Class should load');
    self::assertTrue(class_exists('My_Test_Class'), 'Class should be loaded now');
}

The first line will trigger the regular autoloader, and it should fail to load your test class. The second line uses the autoloader under test. The third line verifies that it was actually loaded.

Upvotes: 0

GordonM
GordonM

Reputation: 31730

Eventually I opted to move the code to disable the normal autoloader to setup() and the code to reenable it to teartown(). The full test suite seems to work fine now.

Upvotes: 0

Related Questions