Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel framework classes not available in PHPUnit data provider

I have something like the following set up in Laravel:

In /app/controllers/MyController.php:

class MyController extends BaseController {

    const MAX_FILE_SIZE = 10000;

    // ....

}

In /app/tests/MyControllerTest.php:

class MyControllerTest extends TestCase {

    public function myDataProvider() {
        return [
            [ MyController::MAX_FILE_SIZE ]
        ];
    }

    /**
     * @dataProvider myDataProvider
     */
    public function testMyController($a) {
        // Just an example
        $this->assertTrue(1 == 1);
    }
}

However, when I run vendor/bin/phpunit I get the following error:

PHP Fatal error:  Class 'Controller' not found in /home/me/my-app/app/controllers/BaseController.php on line 3

Fatal error: Class 'Controller' not found in /home/me/my-app/app/controllers/BaseController.php on line 3

If I remove the reference to the MyController class in myDataProvider() and replace it with a literal constant then the test completes successfully.

In addition, I can place references to MyController::MAX_FILE_SIZE inside the actual testMyController() method, and the test also completes successfully.

It appears that the autoloading setup for Laravel framework classes isn't being set up until after the data provider method is being called, but before the actual test methods are called. Is there any way around this so that I can access Laravel framework classes from within a PHPUnit data provider?


NOTE: I'm calling PHPUnit directly from the command line and not from within an IDE (such as NetBeans). I know some people have had issues with that, but I don't think that applies to my problem.

like image 435
FixMaker Avatar asked Nov 03 '14 08:11

FixMaker


1 Answers

As implied in this answer, this appears to be related to the order that PHPUnit will call any data providers and the setUp() method in any test cases.

PHPUnit will call the data provider methods before running any tests. Before each test it will also call the setUp() method in the test case. Laravel hooks into the setUp() method to call $this->createApplication() which will add the controller classes to the 'include path' so that they can be autoloaded correctly.

Since the data provider methods are run before this happens then any references to controller classes inside a data provider fail. It's possible work around this by modifying the test class to something like this:

class MyControllerTest extends TestCase {

    public function __construct($name = null, array $data = array(), $dataName = '') {
        parent::__construct($name, $data, $dataName);

        $this->createApplication();
    }

    public function myDataProvider() {
        return [
            [ MyController::MAX_FILE_SIZE ]
        ];
    }

    /**
     * @dataProvider myDataProvider
     */
    public function testMyController($a) {
        // Just an example
        $this->assertTrue(1 == 1);
    }
}

This will call createApplication() before the data provider methods are run, and so there is a valid application instance that will allow the appropriate classes to be autoloaded correctly.

This seems to work, but I'm not sure if it's the best solution, or if it is likely to cause any issues (although I can't think of any reasons why it should).

like image 107
FixMaker Avatar answered Oct 19 '22 14:10

FixMaker