在 Laravel 中设置 PHPUnit 测试
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33754043/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Setting up PHPUnit tests in Laravel
提问by Phil Cross
I'm fairly new to unit testing, but I've read pretty much all the documentation on phpunit.de(Up to chapter 10).
我对单元测试还很陌生,但我已经阅读了几乎所有关于phpunit.de的文档(直到第 10 章)。
It states that testing using databases can be slow, but if setup correctly, it can be just as fast as non-database testing.
它指出使用数据库进行测试可能会很慢,但如果设置正确,它可以与非数据库测试一样快。
As such, I want to test a model in Laravel. I've created a model factory to seed data into the database.
因此,我想在 Laravel 中测试一个模型。我创建了一个模型工厂来将数据播种到数据库中。
I've also created a basic test.
我还创建了一个基本测试。
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()
.
在 PHPUnits 文档中,它指出在每次测试之前,都会setUp()
调用该方法来设置测试。还有另一个静态方法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.
我只想为我的数据库表设置一次种子,并在我的测试中使用这些记录。所以我使用 Laravelfactory()
函数从setUpBeforeClass()
方法中为数据库做种。
This is my code:
这是我的代码:
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);
}
}
However, when I run phpunit
, I get the following error:
但是,当我运行时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
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?
如果我将代码从setUpBeforeClass()
to移动setUp()
并运行它,它会按预期工作,但是这肯定是低效的,因为它为每个测试播种数据库?
My questions:
我的问题:
- Is seeding the database from within the
setUpBeforeClass()
the correct way to do this? - 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()
? - If I do have to place the code in the
setUp()
method, are there going to be performance issues? - Should I even be seeding from the
setUpBeforeClass()
orsetUp()
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()
以正确的方式在数据库中播种? - 如果是(问题 1),那么为什么我在运行 phpunit 时会收到致命错误,在调用之前我应该做些什么
factory()
? - 如果我必须将代码放在
setUp()
方法中,是否会有性能问题? - 我什至应该从
setUpBeforeClass()
或setUp()
方法中播种吗?在 Laravel 文档中,它显示了在测试本身中进行播种的示例,但是如果我正在运行 100 个测试(例如),播种 100 次是否是个好主意?
回答by Phil Cross
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.
好的,经过一些调查(类),我确定在调用静态setUpBeforeClass()
方法时 Laravel 应用程序尚未创建。
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.
Laravel 容器在第一次setUp()
被调用时创建\vendor\laravel\framework\src\illuminate\Foundation\Testing\TestCase.php
。这就是为什么当我将代码移动到setUp()
方法时它工作正常的原因。
The container is then stored in the $app
property stored in \vendor\laravel\framework\src\illuminate\Foundation\Testing\ApplicationTrait.php
.
然后将容器存储在存储在 中的$app
属性中\vendor\laravel\framework\src\illuminate\Foundation\Testing\ApplicationTrait.php
。
I can manually create a container instance by adding this code to the setUpBeforeClass()
method:
我可以通过将此代码添加到setUpBeforeClass()
方法中来手动创建容器实例:
$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.
但是这种方法看起来很hacky,我不喜欢它。
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:
相反,我将播种代码移至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();
}
}
}
In combination with Laravels DatabaseMigrations
trait for testing, This is now the workflow:
结合 LaravelDatabaseMigrations
特性进行测试,现在的工作流程如下:
- PHPUnit is called
- The Test class is called, which contains the
DatabaseMigrations
trait - The database is migrated (tables created)
- The
setUp()
method is called for the first time, which seeds the relevant tables with testing data - The test is run, and and access the test data
- There is no
tearDown()
method invoked, instead theDatabaseMigrations
trait simply resets the database, so my test doesn't have to worry about cleaning up the test data.
- PHPUnit 被称为
- 调用 Test 类,其中包含
DatabaseMigrations
特征 - 迁移数据库(创建表)
- 该
setUp()
方法第一次被调用,它用测试数据为相关表做种子 - 运行测试,并访问测试数据
- 没有
tearDown()
调用任何方法,而是DatabaseMigrations
trait 简单地重置数据库,所以我的测试不必担心清理测试数据。
EDIT
编辑
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:
此外,似乎(虽然我不是 100%),如果您有自定义setUp()
方法,则需要runDatabaseMigrations()
从覆盖的setUp()
方法中手动调用:
public function setUp()
{
parent::setUp();
$this->runDatabaseMigrations();
/** Rest of Setup **/
}
runDatabaseMigrations()
doesn't seem to get called automatically if you overload the setUp()
method.
runDatabaseMigrations()
如果您重载该setUp()
方法,似乎不会自动调用。
I hope this helps, but if anyone else has a better solution, please feel free to let me know :)
我希望这会有所帮助,但如果其他人有更好的解决方案,请随时告诉我:)