私はユニットテストにかなり慣れていませんが、 phpunit.de (第10章まで)に関するほとんどすべてのドキュメントを読みました。
データベースを使用したテストは遅くなる可能性があると記載されていますが、正しくセットアップされていれば、データベース以外のテストと同じくらい速くなる可能性があります。
そのため、Laravelでモデルをテストしたいと思います。データベースにデータをシードするためのモデルファクトリを作成しました。
基本的なテストも作成しました。
PHPUnitsのドキュメントには、すべてのテストの前に、テストをセットアップするためにsetUp()
メソッドが呼び出されると記載されています。別の静的メソッドsetUpBeforeClass()
もあります。
データベーステーブルを1回だけシードし、テスト内でレコードを使用したいと思います。そこで、Laravels 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()
内からデータベースをシードすることは、これを行う正しい方法ですか?factory()
を呼び出す前にすべきことはありますか?setUp()
メソッドに配置する必要がある場合、パフォーマンスの問題は発生しますか?setUpBeforeClass()
またはsetUp()
メソッドからシードする必要がありますか? Laravelsのドキュメントでは、テスト自体でシードが発生している例が示されていますが、たとえば100個のテストを実行している場合、100回シードすることをお勧めしますか?さて、(クラスを)少し調べた結果、静的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();
しかし、この方法はかなりハッキーなようで、私はそれが好きではありません。
代わりに、シードコードをsetUp
()メソッドに移動しましたが、クラスのプロパティがnullの場合にのみデータベースにシードしました。したがって、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();
}
}
}
テスト用のLaravelsDatabaseMigrations
トレイトと組み合わせて、これがワークフローになりました。
DatabaseMigrations
トレイトを含むTestクラスが呼び出されますsetUp()
メソッドが初めて呼び出され、関連するテーブルにテストデータがシードされますtearDown()
メソッドは呼び出されませんが、代わりにDatabaseMigrations
トレイトはデータベースをリセットするだけなので、テストではテストデータのクリーンアップについて心配する必要はありません。[〜#〜]編集[〜#〜]
さらに、(私は100%ではありませんが)カスタムsetUp()
メソッドがある場合は、オーバーライドされたrunDatabaseMigrations()
メソッドからsetUp()
を手動で呼び出す必要があるようです。 :
public function setUp()
{
parent::setUp();
$this->runDatabaseMigrations();
/** Rest of Setup **/
}
runDatabaseMigrations()
メソッドをオーバーロードすると、setUp()
が自動的に呼び出されないようです。
これがお役に立てば幸いですが、他にもっと良い解決策がある場合は、遠慮なくお知らせください:)