Setting up PHPUnit tests in Laravel

I am new to unit testing, but I almost completely read all the documentation on phpunit.de (before chapter 10).

It says that testing using databases can be slow, but if configured correctly, it can be as fast as testing without a database.

As such, I want to test the model in Laravel. I created a factory model for sowing data into a database.

I also created a basic test.

The PHPUnits documentation says that before each test, the setUp() method is setUp() to configure the test. There is also another static setUpBeforeClass() method.

I want to align the database table only once and use the records in my test. Therefore, I used the Laravels factory() function to seed the database from the setUpBeforeClass() method.

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:

 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 certainly it is inefficient because it visits the database for each test?

My questions:

  • Selects the database from setUpBeforeClass() correct path for this?
  • If this is (question 1), then why am I getting a fatal error when running phpunit, and is there anything I should do before calling factory() ?
  • If I need to put the code in the setUp() method, will there be performance problems?
  • Should I be seeded from setUpBeforeClass() or setUp() methods? The Laravels documentation provides examples where seeding occurs in the test itself, but if I perform 100 tests (for example), is it recommended to visit 100 times?
+4
source share
1 answer

Well, after a little research (of the classes), I decided that the Laravel application was not yet created by calling the setUpBeforeClass() static method.

The Laravel container is created for the first time when setUp() is called in \vendor\laravel\framework\src\illuminate\Foundation\Testing\TestCase.php . Therefore, it works fine when I go to my setUp() method.

Then the container is stored in the $app property, stored in \vendor\laravel\framework\src\illuminate\Foundation\Testing\ApplicationTrait.php .

I can manually create an instance of the container by adding this code to the setUpBeforeClass() method:

 $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.

Instead, I moved the seeding code to the setUp () method, but only sowed the database if the class properties were empty. Therefore, it is only visited the first time setUp() called. Any subsequent calls are not attended:

 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(); } } } 

Combined with the Laravels DatabaseMigrations feature for testing, this is now a workflow:

  • Phpunit is called
  • The Test class is called, which contains the DatabaseMigrations attribute.
  • Database Migration (Created Tables)
  • The setUp() method is called for the first time, which seeds the corresponding tables with test data
  • The test runs and accesses the test data.
  • There is no tearDown() method; instead, the DatabaseMigrations flag simply flushes the database, so my test does not need to worry about clearing the test data.

EDIT

Also, it seems (although I'm not 100%) that if you have your own setUp() method, you need to manually call runDatabaseMigrations() from the overridden setUp() method:

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

runDatabaseMigrations() does not seem to be automatically called if you overload the setUp() method.

I hope this helps, but if anyone else has a better solution, please let me know :)

+13
source

All Articles