Skip to content

Instantly share code, notes, and snippets.

@slava-vishnyakov
Forked from adamwathan/DatabaseSetup.php
Last active August 5, 2022 00:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slava-vishnyakov/2ae1d1e030204ad10c1898b20aeba4cb to your computer and use it in GitHub Desktop.
Save slava-vishnyakov/2ae1d1e030204ad10c1898b20aeba4cb to your computer and use it in GitHub Desktop.
A Better Database Testing Workflow in Laravel
<?php
use Illuminate\Contracts\Console\Kernel;
trait DatabaseSetup
{
protected static $migrated = false;
public function setupDatabase()
{
if ($this->isInMemory()) {
$this->setupInMemoryDatabase();
} else {
$this->setupTestDatabase();
}
}
protected function isInMemory()
{
return config('database.connections')[config('database.default')]['database'] == ':memory:';
}
protected function setupInMemoryDatabase()
{
$this->artisan('migrate');
$this->app[Kernel::class]->setArtisan(null);
}
protected function setupTestDatabase()
{
if (!static::$migrated) {
$this->whenMigrationsChange(function() {
$this->artisan('migrate:refresh');
$this->app[Kernel::class]->setArtisan(null);
});
static::$migrated = true;
}
$this->beginDatabaseTransaction();
}
public function beginDatabaseTransaction()
{
$database = $this->app->make('db');
foreach ($this->connectionsToTransact() as $name) {
$database->connection($name)->beginTransaction();
}
$this->beforeApplicationDestroyed(function () use ($database) {
foreach ($this->connectionsToTransact() as $name) {
$database->connection($name)->rollBack();
}
});
}
protected function connectionsToTransact()
{
return property_exists($this, 'connectionsToTransact')
? $this->connectionsToTransact : [null];
}
protected function getMigrationsMd5()
{
return md5(collect(glob(base_path('database/migrations/*')))
->map(function ($f) {
return file_get_contents($f);
})->implode(''));
}
protected function whenMigrationsChange($callback)
{
$md5 = $this->getMigrationsMd5();
$path = storage_path('app/migrations_md5.txt');
if(!file_exists($path) || ($md5 !== file_get_contents($path))) {
$callback();
file_put_contents($path, $md5);
}
}
}
<?php
abstract class TestCase extends Illuminate\Foundation\Testing\TestCase
{
use DatabaseSetup;
/**
* The base URL to use while testing the application.
*
* @var string
*/
protected $baseUrl = 'http://localhost';
protected function setUp()
{
parent::setUp();
$this->setupDatabase();
}
/**
* Creates the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
return $app;
}
}
@Solomon04
Copy link

Thank you for this

@nerdo
Copy link

nerdo commented Aug 4, 2022

Very cool.

@nerdo
Copy link

nerdo commented Aug 5, 2022

For slightly larger codebases with lots of migrations, this getMigrationsMd5() function should be a tiny bit faster.

It uses the file mtimes instead of the file contents. Assuming the mtime changes along with the contents, the effect should be the same without having the extra I/O of reading each file.

    protected function getMigrationsMd5()
    {
        return md5(
            collect(glob(base_path('database/migrations/*')))
                ->map(function ($f) {
                    return $f . filemtime($f);
                })
                ->implode('')
        );
    }

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