Skip to content

Instantly share code, notes, and snippets.

@dgoguerra
Last active May 17, 2019 12:43
Show Gist options
  • Save dgoguerra/510a4db39bda8ab6333b3116a8520d5e to your computer and use it in GitHub Desktop.
Save dgoguerra/510a4db39bda8ab6333b3116a8520d5e to your computer and use it in GitHub Desktop.
Restart and migrate database once before all phpunit tests. Optionally, use a tratit to restart between each test.
<?php
use Illuminate\Contracts\Console\Kernel;
require __DIR__ . '/../vendor/autoload.php';
// Initialize Laravel application
$app = require __DIR__.'/../bootstrap/app.php';
$artisan = $app->make(Kernel::class);
$artisan->bootstrap();
// php artisan test:restart-database
$exitCode = $artisan->call('test:restart-database', [
'--ansi' => true,
]);
// Print the command's output
echo $artisan->output();
// If the command errored, exit with error
if ($exitCode !== 0) {
exit(1);
}
diff --git a/config/database.php b/config/database.php
index 9184f0c..d486dec 100644
--- a/config/database.php
+++ b/config/database.php
@@ -54,6 +54,21 @@ return [
'engine' => null,
],
+ 'testing' => [
+ 'driver' => 'mysql',
+ 'host' => env('DB_HOST', '127.0.0.1'),
+ 'port' => env('DB_PORT', '3306'),
+ 'database' => env('DB_DATABASE', 'forge').'_test',
+ 'username' => env('DB_USERNAME', 'forge'),
+ 'password' => env('DB_PASSWORD', ''),
+ 'unix_socket' => env('DB_SOCKET', ''),
+ 'charset' => 'utf8mb4',
+ 'collation' => 'utf8mb4_unicode_ci',
+ 'prefix' => '',
+ 'strict' => true,
+ 'engine' => null,
+ ],
+
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', '127.0.0.1'),
diff --git a/phpunit.xml b/phpunit.xml
index 5273873..5f1a6b1 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
- bootstrap="vendor/autoload.php"
+ bootstrap="tests/bootstrap-phpunit.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
@@ -23,13 +23,13 @@
</whitelist>
</filter>
<php>
- <env name="APP_ENV" value="testing"/>
- <env name="APP_KEY" value="base64:gvhlk/E8jhHhcgHPzKR5XCdu3i30wIZScndosFMbGws="/>
- <env name="DB_CONNECTION" value="testing"/>
- <env name="BCRYPT_ROUNDS" value="4"/>
- <env name="CACHE_DRIVER" value="array"/>
- <env name="SESSION_DRIVER" value="array"/>
- <env name="QUEUE_DRIVER" value="sync"/>
- <env name="MAIL_DRIVER" value="array"/>
+ <env name="APP_ENV" value="testing" force="true"/>
+ <env name="APP_KEY" value="base64:gvhlk/E8jhHhcgHPzKR5XCdu3i30wIZScndosFMbGws=" force="true"/>
+ <env name="DB_CONNECTION" value="testing" force="true"/>
+ <env name="BCRYPT_ROUNDS" value="4" force="true"/>
+ <env name="CACHE_DRIVER" value="array" force="true"/>
+ <env name="SESSION_DRIVER" value="array" force="true"/>
+ <env name="QUEUE_DRIVER" value="sync" force="true"/>
+ <env name="MAIL_DRIVER" value="array" force="true"/>
</php>
</phpunit>
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class RestartDatabase extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'test:restart-database';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$dbName = DB::getDatabaseName();
if (!preg_match("/_test$/", $dbName)) {
$this->error("Command 'test:restart-database' expects the database name"
. " to end in '_test' to avoid deleting real data. The current"
. " database is '$dbName', something may be misconfigured.");
return 1;
}
$tables = $this->listAllTables();
if (count($tables)) {
$this->deleteTables($tables);
}
$this->call('migrate');
$this->call('passport:install');
}
/**
* List all tables of the current database.
*
* @return array
*/
protected function listAllTables()
{
$rows = DB::select('SHOW TABLES');
return array_column($rows, 'Tables_in_'.DB::getDatabaseName());
}
/**
* Delete tables of the database.
*
* The deletion is wrapped in a transaction to avoid
* making any deletions if something fails.
*
* @param array $tables
* @return void
*/
protected function deleteTables(array $tables)
{
DB::beginTransaction();
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
DB::statement('DROP TABLE '.implode(',', $tables));
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
DB::commit();
}
}
<?php
namespace Tests;
trait RestartsDatabase
{
/**
* Define hook to restart the database before each test.
*
* Will be called from CreatesApplication::setUpTraits().
*
* @return void
* @throws \Exception
*/
public function restartDatabase()
{
$this->artisan('test:restart-database');
}
}
diff --git i/tests/TestCase.php w/tests/TestCase.php
index c58ad54..d9d2faf 100644
--- i/tests/TestCase.php
+++ w/tests/TestCase.php
@@ -9,4 +9,24 @@ abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
use DatabaseTransactions;
+// use RestartsDatabase;
+
+ /**
+ * Boot the app's testing helper traits.
+ *
+ * Extends how Laravel sets up its default testing traits.
+ *
+ * @return array
+ * @throws \Exception
+ */
+ protected function setUpTraits()
+ {
+ $uses = parent::setUpTraits();
+
+ if (isset($uses[RestartsDatabase::class])) {
+ $this->restartDatabase();
+ }
+
+ return $uses;
+ }
}
@dgoguerra
Copy link
Author

TODO: throw Exception if APP_ENV !== testing or DB::getDatabaseName() is not a database ending in _test. Something like that, to avoid running this trait in a database which is not meant to be thrown away.

@dgoguerra
Copy link
Author

Done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment