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

时间:2014-11-03 08:48:43

标签: php laravel phpunit automated-tests

我在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 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)调用。我知道有些人对此有疑问,但我认为这不适用于我的问题。

3 个答案:

答案 0 :(得分:18)

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

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

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

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(),因此有一个有效的应用程序实例将允许正确自动加载相应的类。

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

答案 1 :(得分:11)

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

import win32api
import win32file
if sys.platform == 'win32':
    return [drive for drive in win32api.GetLogicalDriveStrings() if
                re.match(r"^[A-Z]+", drive) and win32file.GetDriveType(drive + ":" + "//") == win32file.DRIVE_FIXED]

答案 2 :(得分:1)

其他解决方案的

性能警告(尤其是如果您打算在dataProviders中使用工厂的话):

this article所述:

  

测试运行器通过扫描所有测试来构建测试套件   目录[…]当   找到@dataProvider批注,引用的数据提供者为   执行后,将创建一个TestCase并将其添加到TestSuite中,以用于   提供程序中的每个数据集。

     

[…]

     

如果您在数据提供程序中使用工厂方法,则这些   工厂将使用此数据提供程序为每个测试运行一次   在您进行首次测试之前,甚至需要运行。因此,十项测试使用的数据提供者[…]   将在您第一次运行之前运行十次   测试甚至运行。这可能会大大减慢时间,直到您   第一次测试执行。甚至[…]使用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);
        }],
        ...
    ];
}