diff --git a/src/Abstracts/Tests/PhpUnit/TestCase.php b/src/Abstracts/Tests/PhpUnit/TestCase.php index 85384a80..5663f8d5 100644 --- a/src/Abstracts/Tests/PhpUnit/TestCase.php +++ b/src/Abstracts/Tests/PhpUnit/TestCase.php @@ -6,6 +6,7 @@ use Apiato\Core\Traits\TestCaseTrait; use Apiato\Core\Traits\TestTraits\PhpUnit\TestAssertionHelperTrait; use Apiato\Core\Traits\TestTraits\PhpUnit\TestAuthHelperTrait; +use Apiato\Core\Traits\TestTraits\PhpUnit\TestDatabaseProfilerTrait; use Apiato\Core\Traits\TestTraits\PhpUnit\TestRequestHelperTrait; use Illuminate\Contracts\Console\Kernel; use Illuminate\Foundation\Testing\LazilyRefreshDatabase; @@ -20,6 +21,7 @@ abstract class TestCase extends LaravelTestCase use TestAssertionHelperTrait; use HashIdTrait; use LazilyRefreshDatabase; + use TestDatabaseProfilerTrait; /** * The base URL to use while testing the application. diff --git a/src/Traits/TestTraits/PhpUnit/TestDatabaseProfilerTrait.php b/src/Traits/TestTraits/PhpUnit/TestDatabaseProfilerTrait.php new file mode 100644 index 00000000..95b007af --- /dev/null +++ b/src/Traits/TestTraits/PhpUnit/TestDatabaseProfilerTrait.php @@ -0,0 +1,149 @@ +app->make('db')->enableQueryLog(); + } + + /** + * Stop profiling database queries. + */ + protected function stopDatabaseQueryLog(): void + { + $this->app->make('db')->disableQueryLog(); + } + + /** + * Get the database queries. + */ + protected function getDatabaseQueries(): array + { + return $this->app->make('db')->getQueryLog(); + } + + /** + * Dump the database queries. + */ + protected function dumpDatabaseQueries(): void + { + foreach ($this->getDatabaseQueries() as $query) { + dump($query['query']); + } + } + + /** + * Dump and die the database queries. + */ + protected function ddDatabaseQueries(): never + { + dd($this->getDatabaseQueries()); + } + + /** + * Assert the number of database queries. + */ + protected function assertDatabaseQueryCount(int $expectedCount): void + { + $actualCount = count($this->getDatabaseQueries()); + $this->assertEquals($expectedCount, $actualCount, "Expected $expectedCount database queries, but got $actualCount."); + } + + /** + * Assert that the database queries contain the expected query. + */ + protected function assertDatabaseExecutedQuery(string $expectedQuery): void + { + $queries = $this->getDatabaseQueries(); + + $found = false; + foreach ($queries as $query) { + if (str_contains($query['query'], $expectedQuery)) { + $found = true; + break; + } + } + + $this->assertTrue($found, "Expected query '$expectedQuery' not found in database queries."); + } + + /** + * Assert that the database queries contain the expected queries. + */ + protected function assertDatabaseExecutedQueries(array $expectedQueries): void + { + $queries = $this->getDatabaseQueries(); + + foreach ($expectedQueries as $expectedQuery) { + $found = false; + foreach ($queries as $query) { + if (str_contains($query['query'], $expectedQuery)) { + $found = true; + break; + } + } + + $this->assertTrue($found, "Expected query '$expectedQuery' not found in database queries."); + } + } + + /** + * Wrapper to profile database queries. + */ + protected function profileDatabaseQueries(callable $callback): mixed + { + try { + $this->startDatabaseQueryLog(); + $result = $callback(); + } finally { + $this->stopDatabaseQueryLog(); + } + + return $result; + } + + /** + * Wrapper to profile database queries and assert the number of queries. + */ + protected function profileDatabaseQueryCount(int $expectedCount, callable $callback): mixed + { + return $this->profileDatabaseQueries(function () use ($expectedCount, $callback) { + $result = $callback(); + $this->assertDatabaseQueryCount($expectedCount); + + return $result; + }); + } + + /** + * Wrapper to profile database queries and assert the executed query. + */ + protected function profileDatabaseExecutedQuery(string $expectedQuery, callable $callback): mixed + { + return $this->profileDatabaseQueries(function () use ($expectedQuery, $callback) { + $result = $callback(); + $this->assertDatabaseExecutedQuery($expectedQuery); + + return $result; + }); + } + + /** + * Wrapper to profile database queries and assert the executed queries. + */ + protected function profileDatabaseExecutedQueries(array $expectedQueries, callable $callback): mixed + { + return $this->profileDatabaseQueries(function () use ($expectedQueries, $callback) { + $result = $callback(); + $this->assertDatabaseExecutedQueries($expectedQueries); + + return $result; + }); + } +} diff --git a/tests/Unit/Abstracts/Tests/PhpUnit/TestCaseTest.php b/tests/Unit/Abstracts/Tests/PhpUnit/TestCaseTest.php new file mode 100644 index 00000000..ccd5f153 --- /dev/null +++ b/tests/Unit/Abstracts/Tests/PhpUnit/TestCaseTest.php @@ -0,0 +1,35 @@ +assertContains($trait, class_uses_recursive(TestCase::class)); + } + } +} diff --git a/tests/Unit/Traits/TestTraits/PhpUnit/TestDatabaseProfilerTraitTest.php b/tests/Unit/Traits/TestTraits/PhpUnit/TestDatabaseProfilerTraitTest.php new file mode 100644 index 00000000..6a4bcca1 --- /dev/null +++ b/tests/Unit/Traits/TestTraits/PhpUnit/TestDatabaseProfilerTraitTest.php @@ -0,0 +1,475 @@ +enableQueryLog(); + + $this->startDatabaseQueryLog(); + } + + public function testStopDatabaseQueryLog(): void + { + DB::expects()->disableQueryLog(); + + $this->stopDatabaseQueryLog(); + } + + public function testGetDatabaseQueries(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->assertEquals($queries, $this->getDatabaseQueries()); + } + + public function testAssertDatabaseQueryCount(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->assertDatabaseQueryCount(2); + } + + public function testAssertDatabaseQueryCountWithDifferentCount(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage('Expected 3 database queries, but got 2.'); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->assertDatabaseQueryCount(3); + } + + public function testAssertDatabaseQueryCountWithEmptyDatabaseQueries(): void + { + $queries = []; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->assertDatabaseQueryCount(0); + } + + public function testAssertDatabaseExecutedQuery(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->assertDatabaseExecutedQuery('query1'); + } + + public function testAssertDatabaseExecutedQueryWithDifferentQuery(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query3' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->assertDatabaseExecutedQuery('query3'); + } + + public function testAssertDatabaseExecutedQueryWithEmptyDatabaseQueries(): void + { + $queries = []; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query1' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->assertDatabaseExecutedQuery('query1'); + } + + public function testAssertDatabaseExecutedQueryWithEmptyQueries(): void + { + $queries = []; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query '' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->assertDatabaseExecutedQuery(''); + } + + public function testAssertDatabaseExecutedQueryWithDifferentQueriesAndEmptyDatabaseQueries(): void + { + $queries = []; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query1' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->assertDatabaseExecutedQuery('query1'); + } + + public function testAssertDatabaseExecutedQueryWithEmptyQueriesAndDatabaseQueries(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->assertDatabaseExecutedQuery(''); + } + + public function testAssertDatabaseExecutedQueries(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2'], ['query' => 'query3']]; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->assertDatabaseExecutedQueries(['query1', 'query2']); + } + + public function testAssertDatabaseExecutedQueriesWithDifferentQueries(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query3' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->assertDatabaseExecutedQueries(['query1', 'query3']); + } + + public function testAssertDatabaseExecutedQueriesWithDifferentQueries2(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query4' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->assertDatabaseExecutedQueries(['query4', 'query3']); + } + + public function testAssertDatabaseExecutedQueriesWithEmptyQueries(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->assertDatabaseExecutedQueries([]); + } + + public function testAssertDatabaseExecutedQueriesWithEmptyDatabaseQueries(): void + { + $queries = []; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query1' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->assertDatabaseExecutedQueries(['query1']); + } + + public function testAssertDatabaseExecutedQueriesWithEmptyQueriesAndDatabaseQueries(): void + { + $queries = []; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->assertDatabaseExecutedQueries([]); + } + + public function testAssertDatabaseExecutedQueriesWithDifferentQueriesAndEmptyDatabaseQueries(): void + { + $queries = []; + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query1' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->assertDatabaseExecutedQueries(['query1']); + } + + public function testProfileDatabaseQueries(): void + { + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + + $this->profileDatabaseQueries(function () { + // Do nothing + }); + } + + public function testProfileDatabaseQueriesRunsClosure(): void + { + $closureRan = false; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + + $this->profileDatabaseQueries(function () use (&$closureRan) { + $closureRan = true; + }); + + $this->assertTrue($closureRan); + } + + public function testProfileDatabaseQueriesWithException(): void + { + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + + $this->expectException(\Exception::class); + $this->profileDatabaseQueries(function () { + throw new \Exception(); + }); + } + + public function testProfileDatabaseQueryCount(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->profileDatabaseQueryCount(2, function () { + // Do nothing + }); + } + + public function testProfileDatabaseQueryCountWithDifferentCount(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage('Expected 3 database queries, but got 2.'); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->profileDatabaseQueryCount(3, function () { + // Do nothing + }); + } + + public function testProfileDatabaseQueryCountRunsClosure(): void + { + $closureRan = false; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn([]); + + $this->profileDatabaseQueryCount(0, function () use (&$closureRan) { + $closureRan = true; + }); + + $this->assertTrue($closureRan); + } + + public function testProfileDatabaseQueryCountWithException(): void + { + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + + $this->expectException(\Exception::class); + $this->profileDatabaseQueryCount(0, function () { + throw new \Exception(); + }); + } + + public function testProfileDatabaseExecutedQuery(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->profileDatabaseExecutedQuery('query1', function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueryWithDifferentQuery(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query3' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->profileDatabaseExecutedQuery('query3', function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueryWithEmptyDatabaseQueries(): void + { + $queries = []; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query1' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->profileDatabaseExecutedQuery('query1', function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueryWithEmptyQueries(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->profileDatabaseExecutedQuery('', function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueryWithDifferentQueriesAndEmptyDatabaseQueries(): void + { + $queries = []; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query1' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->profileDatabaseExecutedQuery('query1', function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueryWithEmptyQueriesAndDatabaseQueries(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->profileDatabaseExecutedQuery('', function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueryRunsClosure(): void + { + $closureRan = false; + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->profileDatabaseExecutedQuery('query1', function () use (&$closureRan) { + $closureRan = true; + }); + + $this->assertTrue($closureRan); + } + + public function testProfileDatabaseExecutedQueryWithException(): void + { + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + + $this->expectException(\Exception::class); + $this->profileDatabaseExecutedQuery('query1', function () { + throw new \Exception(); + }); + } + + public function testProfileDatabaseExecutedQueries(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->profileDatabaseExecutedQueries(['query1', 'query2'], function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueriesWithDifferentQueries(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query3' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->profileDatabaseExecutedQueries(['query1', 'query3'], function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueriesWithDifferentQueries2(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query4' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->profileDatabaseExecutedQueries(['query4', 'query3'], function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueriesWithEmptyQueries(): void + { + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->profileDatabaseExecutedQueries([], function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueriesWithEmptyDatabaseQueries(): void + { + $queries = []; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->expectExceptionMessage("Expected query 'query1' not found in database queries."); + $this->expectException(\PHPUnit\Framework\AssertionFailedError::class); + $this->profileDatabaseExecutedQueries(['query1'], function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueriesWithEmptyQueriesAndDatabaseQueries(): void + { + $queries = []; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->profileDatabaseExecutedQueries([], function () { + // Do nothing + }); + } + + public function testProfileDatabaseExecutedQueriesRunsClosure(): void + { + $closureRan = false; + $queries = [['query' => 'query1'], ['query' => 'query2']]; + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + DB::expects()->getQueryLog()->andReturn($queries); + + $this->profileDatabaseExecutedQueries(['query1'], function () use (&$closureRan) { + $closureRan = true; + }); + + $this->assertTrue($closureRan); + } + + public function testProfileDatabaseExecutedQueriesWithException(): void + { + DB::expects()->enableQueryLog(); + DB::expects()->disableQueryLog(); + + $this->expectException(\Exception::class); + $this->profileDatabaseExecutedQueries(['query1'], function () { + throw new \Exception(); + }); + } +}