【问题标题】:Laravel framework classes not available in PHPUnit data providerLaravel 框架类在 PHPUnit 数据提供程序中不可用
【发布时间】:2014-12-29 21:30:26
【问题描述】:

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

【问题讨论】:

    标签: php laravel phpunit automated-tests


    【解决方案1】:

    正如this answer 所暗示的,这似乎与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(),因此有一个有效的应用程序实例将允许正确地自动加载适当的类。

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

    【讨论】:

    • 调用$this->refreshApplication(); 可能更安全,因为这也会将环境设置为testing。你也不一定需要创建一个构造函数,你可以直接在你的数据提供者中调用$this->refreshApplication();(如果你只有我猜的那个)。
    【解决方案2】:

    如果您直接在 dataProvider 方法中创建应用程序,测试的初始化速度会更快,尤其是当您有大量项目要测试时。

    public function myDataProvider() {
        $this->createApplication();
    
        return [
            [ MyController::MAX_FILE_SIZE ]
        ];
    }
    

    【讨论】:

      【解决方案3】:
      其他解决方案的

      性能警告(特别是如果您计划在 dataProviders 中使用工厂):

      正如this article 解释的那样:

      测试运行器通过扫描您的所有测试来构建测试套件 目录 […] 当一个 找到@dataProvider注解,引用的数据提供者是 EXECUTED,然后创建一个 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);
              }],
              ...
          ];
      }
      

      【讨论】:

      • $this->refreshApplication() 在 Laravel 7 中没有帮助。我按照你说的那样使用了闭包,我可以调用工厂。谢谢
      【解决方案4】:

      您可以通过将自定义引导程序添加到您的项目phpunit.xml 来调整 PHPUnit 的这种行为,如下所示(查看第 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>
      

      然后在您的测试文件夹中创建一个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 方法之后运行

      【讨论】:

        猜你喜欢
        • 2017-05-04
        • 1970-01-01
        • 2013-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-22
        相关资源
        最近更新 更多