Laravel 框架类在 PHPUnit 数据提供程序中不可用

2022-01-14 00:00:00 automated-tests php laravel phpunit

我在 Laravel 中有如下设置:

/app/controllers/MyController.php:

类 MyController 扩展 BaseController {常量 MAX_FILE_SIZE = 10000;//....}

/app/tests/MyControllerTest.php:

class MyControllerTest 扩展 TestCase {公共函数 myDataProvider() {返回 [[ MyController::MAX_FILE_SIZE ]];}/*** @dataProvider 我的数据提供者*/公共功能 testMyController($a) {//只是一个例子$this->assertTrue(1 == 1);}}

但是,当我运行 vendor/bin/phpunit 时,我收到以下错误:

<上一页>PHP 致命错误:在第 3 行的/home/me/my-app/app/controllers/BaseController.php 中找不到类控制器"致命错误:在第 3 行的/home/me/my-app/app/controllers/BaseController.php 中找不到类控制器"

如果我在 myDataProvider() 中删除对 MyController 类的引用并将其替换为文字常量,则测试成功完成.

另外,我可以将 MyController::MAX_FILE_SIZE 的引用放在实际的 testMyController() 方法中,测试也成功完成.

Laravel 框架类的自动加载设置似乎直到之后数据提供者方法被调用,但之前实际测试方法是叫.有什么办法可以让我从 PHPUnit 数据提供程序中访问 Laravel 框架类?

<小时>

注意:我直接从命令行调用 PHPUnit,而不是从 IDE(例如 NetBeans)中调用.我知道有些人对此有疑问,但我认为这不适用于我的问题.

解决方案

正如 this answer 中所暗示的,这似乎是与 PHPUnit 调用任何数据提供者的顺序以及任何测试用例中的 setUp() 方法有关.

PHPUnit 将在运行任何测试之前调用数据提供者方法.在每次测试之前,它还会调用测试用例中的 setUp() 方法.Laravel 挂钩到 setUp() 方法以调用 $this->createApplication() 它将控制器类添加到包含路径",以便它们可以自动加载正确.

由于数据提供者方法在此发生之前运行,因此对数据提供者内的控制器类的任何引用都会失败.可以通过将测试类修改为以下内容来解决此问题:

class MyControllerTest 扩展 TestCase {公共函数 __construct($name = null, 数组 $data = array(), $dataName = '') {父::__construct($name, $data, $dataName);$this->createApplication();}公共函数 myDataProvider() {返回 [[ MyController::MAX_FILE_SIZE ]];}/*** @dataProvider 我的数据提供者*/公共功能 testMyController($a) {//只是一个例子$this->assertTrue(1 == 1);}}

这将在数据提供程序方法运行之前调用 createApplication(),因此有一个有效的应用程序实例可以正确地自动加载相应的类.

这似乎可行,但我不确定它是否是最佳解决方案,或者它是否可能导致任何问题(尽管我想不出任何原因).

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.

解决方案

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).

相关文章