Intereting Posts

Настройка тестов PHPUnit в Laravel

Я довольно новичок в модульном тестировании, но я почти полностью прочитал всю документацию на phpunit.de (до главы 10).

В нем говорится, что тестирование с использованием баз данных может быть медленным, но при правильной настройке это может быть так же быстро, как тестирование без базы данных.

Поэтому я хочу протестировать модель в Laravel. Я создал фабрику модели для засева данных в базу данных.

Я также создал базовый тест.

В документации PHPUnits указано, что перед каждым тестом setUp() метод setUp() для настройки теста. Существует еще один статический метод setUpBeforeClass() .

Я хочу выровнять таблицу базы данных только один раз и использовать записи в моем тесте. Поэтому я использовал функцию Laravels factory() для setUpBeforeClass() базы данных из метода 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() код из setUpBeforeClass() в setUp() и запустим его, он будет работать так, как ожидалось, но, конечно же, это неэффективно, поскольку он посещает базу данных для каждого теста?

Мои вопросы:

  1. setUpBeforeClass() базу данных из setUpBeforeClass() правильный способ сделать это?
  2. Если это (вопрос 1), то почему я получаю фатальную ошибку при запуске phpunit, и есть ли что-нибудь, что я должен делать до вызова factory() ?
  3. Если мне нужно поместить код в метод setUp() , будут ли проблемы с производительностью?
  4. Должен ли я даже setUp() методы setUpBeforeClass() или setUp() ? В документации Laravels приводятся примеры, где посев происходит в самом тесте, но если я выполняю 100 тестов (например), рекомендуется ли посещать 100 раз?

Хорошо, после нескольких исследований (классов), я решил, что приложение Laravel еще не создано, когда setUpBeforeClass() статический setUpBeforeClass() .

Контейнер Laravel создается в первый раз, когда setUp() вызывается в \vendor\laravel\framework\src\illuminate\Foundation\Testing\TestCase.php . Вот почему он отлично работает, когда я setUp() к 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 (), но только setUp базу данных, если свойства класса были пустыми. Поэтому он 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(); } } } 

В сочетании с характеристикой Laravels DatabaseMigrations для тестирования, теперь это рабочий процесс:

  1. Вызывается PHPUnit
  2. Вызывается класс Test, который содержит свойство DatabaseMigrations
  3. Миграция базы данных (созданные таблицы)
  4. Метод setUp() вызывается в первый раз, который семена соответствующих таблиц с данными тестирования
  5. Тест выполняется, и доступ к тестовым данным
  6. Не существует tearDown() , вместо этого свойство DatabaseMigrations просто сбрасывает базу данных, поэтому мой тест не должен беспокоиться о очистке тестовых данных.

РЕДАКТИРОВАТЬ

Кроме того, кажется (хотя я не 100%), что если у вас есть собственный setUp() , вам нужно вручную вызвать runDatabaseMigrations() из переопределенного setUp() :

 public function setUp() { parent::setUp(); $this->runDatabaseMigrations(); /** Rest of Setup **/ } 

runDatabaseMigrations() , похоже, не вызывается автоматически, если вы перегружаете метод setUp() .

Я надеюсь, что это поможет, но если у кого-то еще есть лучшее решение, пожалуйста, сообщите мне 🙂