Created
February 13, 2020 15:23
-
-
Save johannesnagl/3bbfae042d85d8949f1cca2d1ce90539 to your computer and use it in GitHub Desktop.
Including this trait in your Laravel Test code will **automatically** start counting SQL queries for every test.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php declare(strict_types = 1); | |
namespace Swat\Api\V2\Tests\Traits; | |
use DB; | |
use Illuminate\Database\Events\QueryExecuted; | |
use Swat\Api\V2\Tests\TestCase; | |
/** | |
* Including this trait will **automatically** start counting SQL queries | |
* for every test. | |
* | |
* Use `$this->assertSqlCount(..);` to test for them. | |
* | |
* @mixin TestCase | |
*/ | |
trait SqlStatementCountAssertionTrait | |
{ | |
/** | |
* Recorded SQL query events | |
* | |
* @var QueryExecuted[] | |
*/ | |
protected $sqlQueryEvents = []; | |
protected function setupSqlCountListenForQueries(): void | |
{ | |
$this->sqlCounterReset(); | |
DB::listen(function (QueryExecuted $event) { | |
$this->sqlQueryEvents[] = $event; | |
}); | |
} | |
/** | |
* Assert the number of SQL queries performed. | |
* | |
* After "reading" this value, the counters are reset. | |
* | |
* @param int $expectedCount | |
* @param string $msg Will only be displayed if the assertion fails | |
*/ | |
protected function assertSqlCount(int $expectedCount, string $msg = ''): void | |
{ | |
$numSqlQueries = \count($this->sqlQueryEvents); | |
if ($expectedCount === $numSqlQueries) { | |
$this->sqlCounterReset(); | |
return; | |
} | |
if ($msg) { | |
$msg .= "\n\n"; | |
} | |
$msg .= sprintf("Expected number of SQL statements of %d does not match the actual value of %d\nQueries:\n\n%s\n", | |
$expectedCount, | |
$numSqlQueries, | |
implode("\n", | |
array_map( | |
function (QueryExecuted $query) { | |
return sprintf('[%s] %s', | |
$query->connectionName, | |
$query->sql | |
); | |
}, | |
$this->sqlQueryEvents | |
) | |
) | |
); | |
$this->assertSame($expectedCount, $numSqlQueries, $msg); | |
} | |
/** | |
* Assert the actual SQL queries (without bindings). | |
* | |
* After "reading" this value, the counters are reset. | |
* | |
* @param string $expectedQueries | |
* @param string $msg Will only be displayed if the assertion fails | |
*/ | |
protected function assertSqlQueries(string $expectedQueries, string $msg = ''): void | |
{ | |
$expectedQueries = trim($expectedQueries); | |
$actualQueries = trim( | |
implode("\n", | |
array_map( | |
function (QueryExecuted $query): string { | |
// Replace any numeric literals with "fake" bind | |
// placeholders. The framework recently optimized | |
// whereIn queries to contain all-only integer | |
// literals directly, which means it includes | |
// IDs which may change during multiple test | |
// runs, which we now manually need to normalize | |
return preg_replace( | |
[ | |
// Covers integers in `WHERE IN ()` | |
'/\d+(,|\))/', | |
// Covers simple `WHERE x =` | |
'/= \d+/', | |
], | |
[ | |
'?$1', | |
'= ?', | |
], | |
$query->sql) . ';'; | |
}, | |
$this->sqlQueryEvents | |
) | |
) | |
); | |
$this->sqlCounterReset(); | |
if (!$msg) { | |
$msg = 'SQL queries mismatch'; | |
} | |
$this->assertSame($expectedQueries, $actualQueries, $msg); | |
} | |
protected function sqlCounterReset(): void | |
{ | |
$this->sqlQueryEvents = []; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment