我在Laravel中设置了类似以下内容的内容:
在/app/controllers/MyController.php
:
class MyController extends BaseController {
const MAX_FILE_SIZE = 10000;
// ....
}
在/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);
}
}
但是,当我运行vendor/bin/phpunit
时,出现以下错误:
PHP 致命错误:在第 3 行的/home/me/my-app/app/controllers/BaseController.php 中找不到类"控制器"致命错误:在第 3 行的/home/me/my-app/app/controllers/BaseController.php 中找不到类"控制器">
如果我删除了对 myDataProvider()
中 MyController
类的引用并将其替换为文字常量,则测试成功完成。
此外,我可以在实际的testMyController()
方法中放置对MyController::MAX_FILE_SIZE
的引用,并且测试也成功完成。
Laravel框架类的自动加载设置似乎直到调用数据提供程序方法之后,但在调用实际测试方法之前才设置。有什么办法可以解决这个问题,以便我可以从 PHPUnit 数据提供程序中访问 Laravel 框架类?
注意:我直接从命令行调用 PHPUnit,而不是从 IDE(如 NetBeans(中调用。我知道有些人对此有问题,但我认为这不适用于我的问题。
正如这个答案所暗示的,这似乎与 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);
}
}
这将在运行数据提供程序方法之前调用createApplication()
,因此存在一个有效的应用程序实例,该实例将允许正确自动加载相应的类。
这似乎有效,但我不确定它是否是最好的解决方案,或者它是否可能导致任何问题(尽管我想不出任何理由(。
如果直接在 dataProvider 方法中创建应用程序,则测试的初始化速度会快得多,尤其是在有大量要测试的项时。
public function myDataProvider() {
$this->createApplication();
return [
[ MyController::MAX_FILE_SIZE ]
];
}
其他解决方案的性能警告(特别是如果您计划在 dataProvider 中使用工厂(:
正如本文所解释的:
测试运行程序通过扫描所有测试来构建测试套件 目录 [...]当一个 找到批注
@dataProvider
,则引用的数据提供程序为 执行,然后创建一个测试用例并将其添加到测试套件中 提供程序中的每个数据集。[...]
如果在数据提供程序中使用工厂方法,则这些 工厂将使用此数据提供程序为每个测试运行一次 在您的第一次测试运行之前。所以一个数据提供者[...]被十个测试使用。 将在您的第一次之前运行十次 测试甚至运行。这可能会大大减慢时间,直到您的 执行第一个测试。甚至 [...] 使用
phpunit --filter
, 每个数据提供程序仍将运行多次。过滤在测试后发生 套件已生成,因此在任何之后 数据提供程序已执行。
上面的文章建议从 dataProvider 返回一个闭包,并在测试中执行它:
/**
* @test
* @dataProvider paymentProcessorProvider
*/
public function user_can_charge_an_amount($paymentProcessorProvider)
{
$paymentProcessorProvider();
$paymentProcessor = $this->app->make(PaymentProviderContract::class);
$paymentProcessor->charge(2000);
$this->assertEquals(2000, $paymentProcessor->totalCharges());
}
public function paymentProcessorProvider()
{
return [
'Braintree processor' => [function () {
$container = Container::getInstance();
$container->bind(PaymentProviderContract::class, BraintreeProvider::class);
}],
...
];
}
自定义引导程序添加到项目中来调整 PHPUnit 的这种行为,phpunit.xml
如下所示(请看第 3 行(:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="tests/bootstrap.php" ← ← ← THIS
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
...
</phpunit>
然后在您的 tests 文件夹中创建一个bootstrap.php
文件(即您上面表示的路径(,并粘贴以下内容:
<?php
use Illuminate'Contracts'Console'Kernel;
require __DIR__ . '/../vendor/autoload.php';
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
您现在可以在数据提供程序中使用Laravel功能,请记住,它们仍然在您的setUp
方法之后运行。