在 Laravel 中设置 PHPUnit 测试
Posted
技术标签:
【中文标题】在 Laravel 中设置 PHPUnit 测试【英文标题】:Setting up PHPUnit tests in Laravel 【发布时间】: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()
并运行它,它会按预期工作,但是这肯定是低效的,因为它为每次测试都为数据库播种?
我的问题:
-
从
setUpBeforeClass()
中为数据库播种是正确的方法吗?
如果是(问题 1),那么为什么我在运行 phpunit 时会出现致命错误,在调用 factory()
之前我应该做些什么?
如果我必须将代码放在setUp()
方法中,会不会出现性能问题?
我是否应该从setUpBeforeClass()
或setUp()
方法播种?在 Laravels 文档中,它显示了在测试本身中进行播种的示例,但是如果我正在运行 100 个测试(例如),那么播种 100 次是个好主意吗?
【问题讨论】:
【参考方案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 进行测试,现在的工作流程如下:
-
PHPUnit 被调用
Test 类被调用,其中包含
DatabaseMigrations
trait
数据库已迁移(已创建表)
第一次调用setUp()
方法,用测试数据播种相关表
测试运行,并访问测试数据
没有调用tearDown()
方法,而是DatabaseMigrations
trait 只是简单地重置数据库,所以我的测试不必担心清理测试数据。
编辑
另外,看起来(虽然我不是 100%),如果你有一个自定义的setUp()
方法,你需要从被覆盖的setUp()
方法中手动调用runDatabaseMigrations()
:
public function setUp()
parent::setUp();
$this->runDatabaseMigrations();
/** Rest of Setup **/
如果重载setUp()
方法,runDatabaseMigrations()
似乎不会被自动调用。
我希望这会有所帮助,但如果其他人有更好的解决方案,请随时告诉我:)
【讨论】:
Phil,知道为什么 setUp 超载时不会触发“runDatabaseMigrations”吗?我查看了代码,似乎无法理解为什么? @Matt 不幸的是我不知道为什么! (如果我的假设是正确的)。我也查看了代码,也无法弄清楚:(这个问题也提到了这个问题:***.com/questions/33584071/… 谢谢!!我自己刚刚遇到这个问题,想知道为什么在 setUpBeforeClass 之前没有设置应用程序。我也在跟踪代码并自己创建一个 SO 问题,直到我发现这个:) @JosephCrawford 很高兴它有帮助:) 我开始测试提到的 runDatabaseMigrations 问题,但正如 github.com/laravel/framework/commit/… 所述,它已在 5.2 中修复以上是关于在 Laravel 中设置 PHPUnit 测试的主要内容,如果未能解决你的问题,请参考以下文章
在 catch 块中设置对象属性时 PHPUnit 测试失败
Laravel PHPUnit 加载 .env.testing 文件
使用 WSL-2 和 Docker 在 PhpStorm 中设置 PHPUnit:无法解析 PHPUnit 版本输出:无法打开输入文件