【问题标题】:Setting up PHPUnit tests in Laravel在 Laravel 中设置 PHPUnit 测试
【发布时间】:2016-02-18 15:40:27
【问题描述】:

我对单元测试还很陌生,但我已经阅读了phpunit.de 上的几乎所有文档(直到第 10 章)。

它指出使用数据库进行测试可能会很慢,但如果设置正确,它可以与非数据库测试一样快。

因此,我想在 Laravel 中测试一个模型。我创建了一个模型工厂来将数据播种到数据库中。

我还创建了一个基本测试。

在 PHPUnits 文档中,它声明在每次测试之前,都会调用 setUp() 方法来设置测试。还有一个静态方法setUpBeforeClass()

我只想为我的数据库表播种一次,并在我的测试中使用这些记录。因此,我使用 Laravel factory() 函数从 setUpBeforeClass() 方法中为数据库播种。

这是我的代码:

class CommentTest extends TestCase
{
    protected static $blog;
    protected static $comments;

    public static function setUpBeforeClass()
    {
        parent::setUpBeforeClass();

        self::$blog = factory(App\Models\Content\Blog::class)->create();
        self::$comments = factory(App\Models\Content\Comment::class, 6)->create();
    }

    public function testSomething()
    {
        $this->assertTrue(true);
    }
}

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

Fatal error: Call to a member function make() on a non-object in \vendor\laravel\framework\src\Illuminate\Foundation\helpers.php on line 54

Call Stack:
    0.0002     240752   1. {main}() \vendor\phpunit\phpunit\phpunit:0
    0.0173    1168632   2. PHPUnit_TextUI_Command::main() \vendor\phpunit\phpunit\phpunit:47
    0.0173    1175304   3. PHPUnit_TextUI_Command->run() \vendor\phpunit\phpunit\src\TextUI\Command.php:100
    2.9397    5869416   4. PHPUnit_TextUI_TestRunner->doRun() \vendor\phpunit\phpunit\src\TextUI\Command.php:149
    2.9447    6077272   5. PHPUnit_Framework_TestSuite->run() \vendor\phpunit\phpunit\src\TextUI\TestRunner.php:440
    2.9459    6092880   6. PHPUnit_Framework_TestSuite->run() \vendor\phpunit\phpunit\src\Framework\TestSuite.php:747
    2.9555    6096160   7. call_user_func:{\vendor\phpunit\phpunit\src\Framework\TestSuite.php:697}() \vendor\phpunit\phpunit\src\Framework\TestSuite.php:697
    2.9555    6096272   8. CommentTest::setUpBeforeClass() \vendor\phpunit\phpunit\src\Framework\TestSuite.php:697
    2.9555    6096480   9. factory() \tests\CommentTest.php:18
    2.9556    6096656  10. app() \vendor\laravel\framework\src\Illuminate\Foundation\helpers.php:350

如果我将代码从 setUpBeforeClass() 移动到 setUp() 并运行它,它会按预期工作,但是这肯定是低效的,因为它为每次测试都为数据库播种?

我的问题:

  1. setUpBeforeClass() 中为数据库播种是正确的方法吗?
  2. 如果是(问题 1),那么为什么我在运行 phpunit 时会出现致命错误,在调用 factory() 之前我应该​​做些什么?
  3. 如果我必须将代码放在setUp() 方法中,会不会出现性能问题?
  4. 我是否应该从setUpBeforeClass()setUp() 方法播种?在 Laravels 文档中,它显示了在测试本身中进行播种的示例,但是如果我正在运行 100 个测试(例如),那么播种 100 次是个好主意吗?

【问题讨论】:

    标签: php laravel phpunit laravel-5.1


    【解决方案1】:

    好的,经过一番调查(类),我确定在调用静态 setUpBeforeClass() 方法时尚未创建 Laravel 应用程序。

    Laravel 容器是在 setUp() 第一次在 \vendor\laravel\framework\src\illuminate\Foundation\Testing\TestCase.php 中调用时创建的。这就是为什么当我将代码移动到 setUp() 方法时它可以正常工作的原因。

    容器然后存储在$app 属性中,该属性存储在\vendor\laravel\framework\src\illuminate\Foundation\Testing\ApplicationTrait.php 中。

    我可以通过将此代码添加到setUpBeforeClass() 方法来手动创建容器实例:

    $app = require __DIR__.'/../bootstrap/app.php';
    $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
    

    但是这种方法看起来很hacky,我不喜欢它。

    相反,我将播种代码移至setUp() 方法,但仅在类属性为空时才播种数据库。因此,它只会在第一次调用setUp() 时播种。任何后续调用都不会被播种:

    class CommentTest extends TestCase
    {
        use DatabaseMigrations;
    
        protected static $blog;
        protected static $comments;
    
        public function setUp()
        {
            parent::setUp();
    
            $this->runDatabaseMigrations();
    
            if (is_null(self::$blog)) {
                self::$blog = factory(App\Models\Content\Blog::class, 1)->create();
                self::$comments = factory(App\Models\Content\Comment::class, 6)->create();
            }
        }
    }
    

    结合 Laravel DatabaseMigrations trait 进行测试,现在的工作流程如下:

    1. PHPUnit 被调用
    2. Test 类被调用,其中包含DatabaseMigrations trait
    3. 数据库已迁移(已创建表)
    4. 第一次调用setUp()方法,用测试数据播种相关表
    5. 测试运行,并访问测试数据
    6. 没有调用tearDown() 方法,而是DatabaseMigrations trait 只是简单地重置数据库,所以我的测试不必担心清理测试数据。

    编辑

    另外,看起来(虽然我不是 100%),如果你有一个自定义的setUp() 方法,你需要从被覆盖的setUp() 方法中手动调用runDatabaseMigrations()

    public function setUp()
    {
        parent::setUp();
        $this->runDatabaseMigrations();
    
        /** Rest of Setup **/
    }
    

    如果重载setUp() 方法,runDatabaseMigrations() 似乎不会被自动调用。

    我希望这会有所帮助,但如果其他人有更好的解决方案,请随时告诉我:)

    【讨论】:

    • Phil,知道为什么 setUp 超载时不会触发“runDatabaseMigrations”吗?我查看了代码,似乎无法理解为什么?
    • @Matt 不幸的是我不知道为什么! (如果我的假设是正确的)。我也查看了代码,也无法弄清楚:(这个问题也提到了这个问题:stackoverflow.com/questions/33584071/…
    • 谢谢!!我自己刚刚遇到这个问题,想知道为什么在 setUpBeforeClass 之前没有设置应用程序。我也在跟踪代码并自己创建一个 SO 问题,直到我发现这个:)
    • @JosephCrawford 很高兴它有帮助:)
    • 我开始测试提到的 runDatabaseMigrations 问题,但正如 github.com/laravel/framework/commit/… 所述,它已在 5.2 中修复
    猜你喜欢
    • 2022-11-20
    • 1970-01-01
    • 2018-03-09
    • 2019-01-19
    • 2016-08-18
    • 2017-01-11
    • 2017-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多