web-dev-qa-db-ja.com

Laravel)でPHPUnitテストを設定する

私はユニットテストにかなり慣れていませんが、 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()に移動して実行すると、期待どおりに機能しますが、すべてのテストでデータベースをシードするため、これは確かに非効率的ですか?

私の質問:

  1. setUpBeforeClass()内からデータベースをシードすることは、これを行う正しい方法ですか?
  2. もしそうなら(質問1)、phpunitを実行しているときに致命的なエラーが発生するのはなぜですか?factory()を呼び出す前にすべきことはありますか?
  3. コードをsetUp()メソッドに配置する必要がある場合、パフォーマンスの問題は発生しますか?
  4. setUpBeforeClass()またはsetUp()メソッドからシードする必要がありますか? Laravelsのドキュメントでは、テスト自体でシードが発生している例が示されていますが、たとえば100個のテストを実行している場合、100回シードすることをお勧めしますか?
11
Phil Cross

さて、(クラスを)少し調べた結果、静的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トレイトと組み合わせて、これがワークフローになりました。

  1. PHPUnitが呼び出されます
  2. DatabaseMigrationsトレイトを含むTestクラスが呼び出されます
  3. データベースが移行されます(テーブルが作成されます)
  4. setUp()メソッドが初めて呼び出され、関連するテーブルにテストデータがシードされます
  5. テストが実行され、テストデータにアクセスします
  6. tearDown()メソッドは呼び出されませんが、代わりにDatabaseMigrationsトレイトはデータベースをリセットするだけなので、テストではテストデータのクリーンアップについて心配する必要はありません。

[〜#〜]編集[〜#〜]

さらに、(私は100%ではありませんが)カスタムsetUp()メソッドがある場合は、オーバーライドされたrunDatabaseMigrations()メソッドからsetUp()を手動で呼び出す必要があるようです。 :

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

    /** Rest of Setup **/
}

runDatabaseMigrations()メソッドをオーバーロードすると、setUp()が自動的に呼び出されないようです。

これがお役に立てば幸いですが、他にもっと良い解決策がある場合は、遠慮なくお知らせください:)

20
Phil Cross