在Laravel中设置PHPUnit测试 [英] Setting up PHPUnit tests in Laravel

查看:90
本文介绍了在Laravel中设置PHPUnit测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm fairly new to unit testing, but I've read pretty much all the documentation on phpunit.de (Up to chapter 10).

它指出,使用数据库进行测试可能会很慢,但是如果设置正确,它的速度将与非数据库测试一样快.

It states that testing using databases can be slow, but if setup correctly, it can be just as fast as non-database testing.

因此,我想在Laravel中测试一个模型.我已经创建了一个模型工厂,用于将数据播种到数据库中.

As such, I want to test a model in Laravel. I've created a model factory to seed data into the database.

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

I've also created a basic test.

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

In PHPUnits documentation, it states that before every test, the setUp() method is called to setup the test. There's also another static method setUpBeforeClass().

我只希望对数据库表进行一次播种,并在测试中使用记录.因此,我使用Laravels factory()函数从setUpBeforeClass()方法中为数据库添加种子.

I want to seed my database table only once, and use the records within my test. So I used Laravels factory() function to seed the database from within the setUpBeforeClass() method.

这是我的代码:

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时,出现以下错误:

However, when I run phpunit, I get the following error:

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()并运行它,它可以按预期工作,但是肯定会因为每次测试都为数据库注入种子而使效率低下吗?

If I move the code from setUpBeforeClass() to setUp() and run it, it works as expected, but surely this is inefficient as its seeding the database for every test?

我的问题:

  1. setUpBeforeClass()内部播种数据库是否正确?
  2. 如果是(问题1),那么为什么在运行phpunit时出现致命错误,在调用factory()之前我应该​​做些什么?
  3. 如果我必须将代码放入setUp()方法中,会不会有性能问题?
  4. 我应该从setUpBeforeClass()setUp()方法中播种吗?在Laravels文档中,它显示了在测试本身中进行播种的示例,但是,如果我正在运行100个测试(例如),则进行100次播种是一个好主意吗?
  1. Is seeding the database from within the setUpBeforeClass() the correct way to do this?
  2. If it is (question 1), then why am I getting the fatal error when running phpunit, and is there anything I should be doing before calling factory()?
  3. If I do have to place the code in the setUp() method, are there going to be performance issues?
  4. Should I even be seeding from the setUpBeforeClass() or setUp() methods? In Laravels documentation it shows examples where the seeding is happening in the test itself, but If i'm running 100 tests (for example), is it a good idea to be seeding 100 times?

推荐答案

好吧,经过一番调查(这些类),我确定在使用静态setUpBeforeClass()方法时,还没有创建Laravel应用程序.叫.

Ok, after a bit of investigating (the classes), I've determined that the Laravel application has not yet been created when the static setUpBeforeClass() method is called.

首次在\vendor\laravel\framework\src\illuminate\Foundation\Testing\TestCase.php中调用setUp()时创建Laravel容器.这就是为什么当我将代码移至setUp()方法时效果很好.

The Laravel container is created the first time setUp() is called in \vendor\laravel\framework\src\illuminate\Foundation\Testing\TestCase.php. That's why it works fine when I move my code to the setUp() method.

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

The container is then stored in the $app property stored in \vendor\laravel\framework\src\illuminate\Foundation\Testing\ApplicationTrait.php.

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

I can manually create a container instance by adding this code to the setUpBeforeClass() method:

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

但是这种方法似乎很笨拙,我不喜欢它.

But this method seems pretty hacky, and I don't like it.

相反,我将种子代码移至了setUp()方法,但是仅在类属性为null时才为数据库注入种子.因此,它仅在setUp()的第一次调用时被植入.随后的任何调用都不会播种:

Instead, I moved the seeding code to the setUp() method, but only seeded the database if the class properties were null. Therefore it only gets seeded on the first call of setUp(). Any subsequent calls do not get seeded:

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();
        }
    }
}

结合Laravels DatabaseMigrations特性进行测试,现在是工作流程:

In combination with Laravels DatabaseMigrations trait for testing, This is now the workflow:

  1. PHPUnit被称为
  2. 将调用Test类,其中包含DatabaseMigrations特质
  3. 数据库已迁移(已创建表)
  4. 第一次调用setUp()方法,该方法将用测试数据填充相关表
  5. 运行测试,并访问测试数据
  6. 没有调用tearDown()方法,相反,DatabaseMigrations特征只是重置数据库,因此我的测试不必担心清理测试数据.
  1. PHPUnit is called
  2. The Test class is called, which contains the DatabaseMigrations trait
  3. The database is migrated (tables created)
  4. The setUp() method is called for the first time, which seeds the relevant tables with testing data
  5. The test is run, and and access the test data
  6. There is no tearDown() method invoked, instead the DatabaseMigrations trait simply resets the database, so my test doesn't have to worry about cleaning up the test data.

编辑

此外,似乎(尽管我不是100%),如果您有自定义的setUp()方法,则需要从覆盖的setUp()方法中手动调用runDatabaseMigrations():

In addition, it seems (although I'm not 100%), that if you have a custom setUp() method, you need to manually call runDatabaseMigrations() from the overridden setUp() method:

public function setUp()
{
    parent::setUp();
    $this->runDatabaseMigrations();

    /** Rest of Setup **/
}

如果重载setUp()方法,

runDatabaseMigrations()似乎不会自动被调用.

runDatabaseMigrations() doesn't seem to get called automatically if you overload the setUp() method.

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

I hope this helps, but if anyone else has a better solution, please feel free to let me know :)

这篇关于在Laravel中设置PHPUnit测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆