How to transfer and sow before a complete test suite in Laravel using a memory database?

I am trying to set up a test environment in my Laravel project. I am using http://packalyst.com/packages/package/mayconbordin/l5-fixtures with json to seed with sqlite in the memory database and call:

Artisan::call('migrate'); Artisan::call('db:seed'); 

in my setUp function, but this is done before each test, which can grow to thousands in this project.

I tried setUpBeforeClass , but that didn't work. I think that there, because the createApplication method is called in each test and that reset the whole application and also does not load devices from json, perhaps for the same reason.

+6
source share
4 answers

Here's how I did it, if someone struggles with the same, I created a testClase base class that inherits from Laravel and did this:

 /** * Creates the application. * * @return \Illuminate\Foundation\Application */ public function createApplication() { return self::initialize(); } private static $configurationApp = null; public static function initialize(){ if(is_null(self::$configurationApp)){ $app = require __DIR__.'/../bootstrap/app.php'; $app->loadEnvironmentFrom('.env.testing'); $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); if (config('database.default') == 'sqlite') { $db = app()->make('db'); $db->connection()->getPdo()->exec("pragma foreign_keys=1"); } Artisan::call('migrate'); Artisan::call('db:seed'); self::$configurationApp = $app; return $app; } return self::$configurationApp; } public function tearDown() { if ($this->app) { foreach ($this->beforeApplicationDestroyedCallbacks as $callback) { call_user_func($callback); } } $this->setUpHasRun = false; if (property_exists($this, 'serverVariables')) { $this->serverVariables = []; } if (class_exists('Mockery')) { Mockery::close(); } $this->afterApplicationCreatedCallbacks = []; $this->beforeApplicationDestroyedCallbacks = []; } 

I have overwritten the createApplication() and tearDown() methods. I modified the first to use the same $app configuration and remove the tearDown() part where it will run $this->app .

Each of my tests should inherit from this TestClass and it.

Everything else did not work. It works even in a memory database, 100 times faster.

if you are dealing with a user session, after you register the user, you will have to log him out, otherwise the user will be registered because the application environment will never be restored, or you can do something like this, update the application every time, when you want:

 protected static $applicationRefreshed = false; /** * Refresh the application instance. * * @return void */ protected function forceRefreshApplication() { if (!is_null($this->app)) { $this->app->flush(); } $this->app = null; self::$configurationApp = null; self::$applicationRefreshed = true; parent::refreshApplication(); } 

And add this to tearDown() before $this->setUphasRun = false; :

 if (self::$applicationRefreshed) { self::$applicationRefreshed = false; $this->app->flush(); $this->app = null; self::$configurationApp = null; } 
+6
source

Option 1

How to set up a database using migration and seeds, and then using database transactions? ( https://laravel.com/docs/5.1/testing#resetting-the-database-after-each-test )

I wanted to set up a test database using the wizard:

 $ php artisan migrate --database=mysql_testing $ php artisan db:seed --database=mysql_testing 

As you can guess, I am using mysql, but I do not understand why this should not work for sqlite. This is how I do it.

config /database.php

First, add the test database information to the config / database.php file according to the current database information.

 'connections' => [ 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', 'strict' => false, ], 'mysql_testing' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', 'localhost'), 'database' => env('DB_TEST_DATABASE'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', 'strict' => false, ], ], 

If you do this like that, be sure to add DB_TEST_DATABASE to the .env file:

 DB_DATABASE=abc DB_TEST_DATABASE=abc_test 

phpunit.xml

Any values ​​set in the phpunit.xml file when overwriting the values ​​specified in the .env file. Therefore, we say that phpunit uses the mysql_testing database connection instead of the mysql database connection.

 <?xml version="1.0" encoding="UTF-8"?> <phpunit> ... <php> ... <env name="DB_CONNECTION" value="mysql_testing"/> </php> 

Testing class

My test classes are as follows:

 class MyTest extends \TestCase { use \Illuminate\Foundation\Testing\DatabaseTransactions; public function testSomething() { 

Option 2

Here the database is reset before each test, so I prefer option 1. But you can make it work the way you like.

I have tried this before and it might work for you.

Tests /TestCase.php Extend the test case to load a new .env file, .env.testing

 <?php class TestCase extends Illuminate\Foundation\Testing\TestCase { /** * The base URL to use while testing the application. * * @var string */ protected $baseUrl = 'http://localhost'; /** * Creates the application. * * @return \Illuminate\Foundation\Application */ public function createApplication() { /** @var $app \Illuminate\Foundation\Application */ $app = require __DIR__.'/../bootstrap/app.php'; $app->loadEnvironmentFrom('.env.testing'); $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); return $app; } } 

.env.testing

Create this new .env file and add the database information

 APP_ENV=testing APP_DEBUG=true APP_KEY=xxx DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_DATABASE=abc_testing DB_USERNAME=xxx DB_PASSWORD=xxx 

In class :

Using PDO to delete and recreate a database is easier than trying to truncate everything. Then use mastering to migrate and seed the database.

 class MyTest extends TestCase { public static function setUpBeforeClass() { $config = parse_ini_file(".env.testing"); $username = $config['DB_USERNAME']; $password = $config['DB_PASSWORD']; $database = $config['DB_DATABASE']; $host = $config['DB_HOST']; // Create test database $connection = new PDO("mysql:host={$host}", $username, $password); $connection->query("DROP DATABASE IF EXISTS " . $database); $connection->query("CREATE DATABASE " . $database); } public function testHomePage() { Artisan::call('migrate'); Artisan::call('db:seed'); $this->visit('/') ->see('Home') ->see('Please sign in') ->dontSee('Logout'); } 
0
source

The main approach in the above solutions is to run all migrations for all tests. I prefer an approach to indicate which migrations and seeds should be run for each test.

This may be more appropriate for large projects, as it can reduce testing time by up to about 70% (using SQL memory in memory, as explained above). For small projects, this may be too perplexing. But anyway...

Use them in TestCase:

 /** * Runs migrations for individual tests * * @param array $migrations * @return void */ public function migrate(array $migrations = []) { $path = database_path('migrations'); $migrator = app()->make('migrator'); $migrator->getRepository()->createRepository(); $files = $migrator->getMigrationFiles($path); if (!empty($migrations)) { $files = collect($files)->filter( function ($value, $key) use ($migrations) { if (in_array($key, $migrations)) { return [$key => $value]; } } )->all(); } $migrator->requireFiles($files); $migrator->runPending($files); } /** * Runs some or all seeds * * @param string $seed * @return void */ public function seed(string $seed = '') { $command = "db:seed"; if (empty($seed)) { Artisan::call($command); } else { Artisan::call($command, ['--class' => $seed]); } } 

Then call migrate () and seed as required in separate tests, for example:

  $this->migrate( [ '2013_10_11_081829_create_users_table', ] ); $this->seed(UserTableSeeder::class); 
0
source

create a file in your testrunner project with this content:

 php artisan migrate:rollback --env=testing php artisan migrate --env=testing --seed vendor/bin/phpunit 

And give permission to execute the command chmod +x testrunner and execute it ./testrunner . Thats all :)

-one
source

All Articles