Я довольно новичок в модульном тестировании, но я почти полностью прочитал всю документацию на 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() и запустим его, он будет работать так, как ожидалось, но, конечно же, это неэффективно, поскольку он посещает базу данных для каждого теста?
Мои вопросы:
setUpBeforeClass() базу данных из setUpBeforeClass() правильный способ сделать это? factory() ? setUp() , будут ли проблемы с производительностью? 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 для тестирования, теперь это рабочий процесс:
DatabaseMigrations setUp() вызывается в первый раз, который семена соответствующих таблиц с данными тестирования tearDown() , вместо этого свойство DatabaseMigrations просто сбрасывает базу данных, поэтому мой тест не должен беспокоиться о очистке тестовых данных. РЕДАКТИРОВАТЬ
Кроме того, кажется (хотя я не 100%), что если у вас есть собственный setUp() , вам нужно вручную вызвать runDatabaseMigrations() из переопределенного setUp() :
public function setUp() { parent::setUp(); $this->runDatabaseMigrations(); /** Rest of Setup **/ }
runDatabaseMigrations() , похоже, не вызывается автоматически, если вы перегружаете метод setUp() .
Я надеюсь, что это поможет, но если у кого-то еще есть лучшее решение, пожалуйста, сообщите мне 🙂