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

Fix*_*ker 13 php phpunit automated-tests laravel

我在Laravel中设置了以下内容:

/app/controllers/MyController.php:

class MyController extends BaseController {

    const MAX_FILE_SIZE = 10000;

    // ....

}
Run Code Online (Sandbox Code Playgroud)

/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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

看来,在调用数据提供程序方法之后,但调用实际测试方法之前,不会设置Laravel框架类的自动加载设置.有没有办法解决这个问题,以便我可以从PHPUnit数据提供程序中访问Laravel框架类?


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

Fix*_*ker 20

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

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

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

这似乎有效,但我不确定它是否是最好的解决方案,或者它是否可能导致任何问题(尽管我不能想出它为什么应该这样做).

  • 调用`$ this-> refreshApplication();`可能更安全,因为这也将环境设置为`testing`.你也不一定需要创建一个构造函数,你可以直接在你的数据提供者中调用`$ this-> refreshApplication();`(如果你只是我猜的那个). (7认同)

小智 13

如果您在dataProvider方法中创建应用程序,测试将初始化得更快,特别是如果您要测试大量项目.

public function myDataProvider() {
    $this->createApplication();

    return [
        [ MyController::MAX_FILE_SIZE ]
    ];
}
Run Code Online (Sandbox Code Playgroud)


Tho*_*axl 5

其他解决方案的性能警告(特别是如果您计划在 dataProvider 中使用工厂):

\n\n

正如本文所解释的:

\n\n
\n

测试运行程序通过扫描所有测试目录 [\xe2\x80\xa6] 来构建测试套件。当 @dataProvider找到注释时,引用的数据提供程序将被执行,然后创建一个测试用例并将其添加到提供程序中每个数据集的 TestSuite。

\n\n

[\xe2\x80\xa6]

\n\n

如果您在数据提供程序中使用工厂方法,则这些工厂将在您的第一个测试运行之前为使用此数据提供程序的每个测试运行一次。因此,在第一个测试运行之前,十个测试使用的数据提供程序 [\xe2\x80\xa6] 将运行十次。这可能会大大减慢执行第一个测试之前的时间。即使 [\xe2\x80\xa6] 使用phpunit --filter,\n 每个数据提供程序仍将运行多次。过滤发生在测试套件生成之后,即任何数据提供程序执行之后。

\n
\n\n

上面的文章建议从 dataProvider 返回一个闭包并在您的测试中执行它:

\n\n
/** \n * @test\n * @dataProvider paymentProcessorProvider\n */\npublic function user_can_charge_an_amount($paymentProcessorProvider)\n{\n    $paymentProcessorProvider();\n    $paymentProcessor = $this->app->make(PaymentProviderContract::class);\n\n    $paymentProcessor->charge(2000);\n\n    $this->assertEquals(2000, $paymentProcessor->totalCharges());\n}\n\npublic function paymentProcessorProvider()\n{\n    return [\n        \'Braintree processor\' => [function () {\n            $container = Container::getInstance();\n            $container->bind(PaymentProviderContract::class, BraintreeProvider::class);\n        }],\n        ...\n    ];\n}\n
Run Code Online (Sandbox Code Playgroud)\n