diff --git a/README.md b/README.md index dd30a09..9dd40e0 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ listed, please submit an issue to this repository! ## Quick Example This example is intended to reflect what should be capable with this library. We're going to -use [cspray/database-testing-phpunit]() as our testing extension, it is ubiquitous and likely the framework you'll start off using with this library. +use [cspray/database-testing-phpunit](https://github.com/cspray/databse-testing-phpunit) as our testing extension, it is ubiquitous and likely the framework you'll start off using with this library. ```php pdo = TestDatabase::connection(); + $this->pdo = self::$testDatabase->connection(); $this->myRepository = new MyRepository($this->pdo); } @@ -79,7 +83,7 @@ final class RepositoryTest extends TestCase { ]) )] public function testTableHasCorrectlyLoadedFixtures() : void { - $table = TestDatabase::table('my_table'); + $table = self::$testDatabase->table('my_table'); self::assertCount(1, $table); @@ -88,7 +92,7 @@ final class RepositoryTest extends TestCase { } public function testTableCanBeReloadedToGetNewlyInsertedRecords() : void { - $table = TestDatabase::table('my_table'); + $table = self::$testDatabase->table('my_table'); self::assertCount(0, $table); @@ -101,5 +105,3 @@ final class RepositoryTest extends TestCase { } ``` - - diff --git a/src/Internal/ConnectionAdapterTestCase.php b/src/Internal/ConnectionAdapterTestCase.php index 3773fc4..f052705 100644 --- a/src/Internal/ConnectionAdapterTestCase.php +++ b/src/Internal/ConnectionAdapterTestCase.php @@ -113,11 +113,11 @@ public function testInsertAfterEstablishingConnectionResultsInAppropriateRecords public function testBeginTransactionAndRollbackResultsInRecordsNotPersisted() : void { $this->connectionAdapter->establishConnection(); -// + $sql = 'SELECT * FROM my_table'; -// $records = $this->executeSelectSql($sql); -// -// self::assertEmpty($records); + $records = $this->executeSelectSql($sql); + + self::assertEmpty($records); $this->connectionAdapter->beginTransaction(); @@ -171,29 +171,26 @@ public function testFetchingTableFromConnectionAdapterAllowsInspectingCorrectRec new SingleRecordFixture('my_table', ['name' => 'Harry']), ]); - $table = $this->connectionAdapter->selectAll('my_table'); + $data = $this->connectionAdapter->selectAll('my_table'); - self::assertSame('my_table', $table->name()); - self::assertCount(1, $table); - self::assertSame('Harry', $table->row(0)->get('name')); + self::assertCount(1, $data); + self::assertSame('Harry', $data[0]['name']); $this->connectionAdapter->insert([ new SingleRecordFixture('my_table', ['name' => 'Mack']), ]); - $table->reload(); + $newData = $this->connectionAdapter->selectAll('my_table'); - self::assertSame('my_table', $table->name()); - self::assertCount(2, $table); - self::assertSame('Harry', $table->row(0)->get('name')); - self::assertSame('Mack', $table->row(1)->get('name')); + self::assertCount(2, $newData); + self::assertSame('Harry', $newData[0]['name']); + self::assertSame('Mack', $newData[1]['name']); $this->connectionAdapter->truncateTable('my_table'); - $table->reload(); + $truncatedData = $this->connectionAdapter->selectAll('my_table'); - self::assertSame('my_table', $table->name()); - self::assertCount(0, $table); + self::assertCount(0, $truncatedData); } diff --git a/src/TestDatabase.php b/src/TestDatabase.php index cad5536..30b5695 100644 --- a/src/TestDatabase.php +++ b/src/TestDatabase.php @@ -3,11 +3,8 @@ namespace Cspray\DatabaseTesting; use Cspray\DatabaseTesting\ConnectionAdapter\ConnectionAdapter; -use Cspray\DatabaseTesting\ConnectionAdapter\ConnectionAdapterFactory; -use Cspray\DatabaseTesting\DatabaseCleanup\CleanupStrategy; use Cspray\DatabaseTesting\DatabaseRepresentation\Table; -use Cspray\DatabaseTesting\Exception\ConnectionAlreadyEstablished; -use Cspray\DatabaseTesting\Exception\ConnectionNotEstablished; +use Cspray\DatabaseTesting\Fixture\Fixture; use Cspray\DatabaseTesting\Internal\ClosureDataProviderTable; /** @@ -18,80 +15,37 @@ */ final class TestDatabase { - private static ?ConnectionAdapter $connectionAdapter = null; - private function __construct( - private readonly string $testClass, - private readonly ConnectionAdapterFactory $connectionAdapterFactory, - private readonly CleanupStrategy $cleanupStrategy, + private readonly ConnectionAdapter $connectionAdapter, ) {} - /** - * @param class-string $class - * @param RequiresTestDatabaseSettings $requiresTestDatabase - * @return self - *@internal - */ - public static function createFromTestCaseRequiresDatabase( - string $class, - RequiresTestDatabaseSettings $requiresTestDatabase - ) : self { - return new self( - $class, - $requiresTestDatabase->connectionAdapterFactory(), - $requiresTestDatabase->cleanupStrategy() - ); - } - - /** - * Allow for introspection of a database table. - * - * @param non-empty-string $name - * @return Table - */ - public static function table(string $name) : Table { - self::verifyConnectionEstablished(__METHOD__); - return new ClosureDataProviderTable($name, fn() => self::$connectionAdapter->selectAll($name)); + public function connectionAdapter() : ConnectionAdapter { + return $this->connectionAdapter; } /** * @template UnderlyingConnection of object * @return UnderlyingConnection */ - public static function connection() : object { - self::verifyConnectionEstablished(__METHOD__); - return self::$connectionAdapter->underlyingConnection(); - } - - public function establishConnection() : void { - if (self::$connectionAdapter !== null) { - throw ConnectionAlreadyEstablished::fromConnectionAlreadyEstablished(); - } - self::$connectionAdapter = $this->connectionAdapterFactory->createConnectionAdapter(); - self::$connectionAdapter->establishConnection(); + public function connection() : object { + return $this->connectionAdapter->underlyingConnection(); } - public function prepareForTest(DatabaseAwareTest $databaseAwareTest) : void { - self::verifyConnectionEstablished(__METHOD__); - $this->cleanupStrategy->cleanupBeforeTest($databaseAwareTest, self::$connectionAdapter); - self::$connectionAdapter->insert($databaseAwareTest->fixtures()); - } - - public function cleanupAfterTest(DatabaseAwareTest $databaseAwareTest) : void { - self::verifyConnectionEstablished(__METHOD__); - $this->cleanupStrategy->teardownAfterTest($databaseAwareTest, self::$connectionAdapter); - } - - public function closeConnection() : void { - self::verifyConnectionEstablished(__METHOD__); - self::$connectionAdapter->closeConnection(); - self::$connectionAdapter = null; + /** + * @param list $fixtures + */ + public function loadFixtures(array $fixtures) : void { + $this->connectionAdapter->insert($fixtures); } - private static function verifyConnectionEstablished(string $method) : void { - if (self::$connectionAdapter === null) { - throw ConnectionNotEstablished::fromInvalidInvocationBeforeConnectionEstablished($method); - } + /** + * Allow for introspection of a database table. + * + * @param non-empty-string $name + * @return Table + */ + public function table(string $name) : Table { + return new ClosureDataProviderTable($name, fn() => $this->connectionAdapter->selectAll($name)); } } \ No newline at end of file diff --git a/tests/Unit/TestDatabaseTest.php b/tests/Unit/TestDatabaseTest.php deleted file mode 100644 index 72f9112..0000000 --- a/tests/Unit/TestDatabaseTest.php +++ /dev/null @@ -1,224 +0,0 @@ -setStaticPropertyValue('connectionAdapter', null); - } - - public function testAttemptingToCallConnectionWithoutProperlyEstablishingConnectionThrowsException() : void { - $this->expectException(ConnectionNotEstablished::class); - $this->expectExceptionMessage( - 'A connection to the test database MUST be established before invoking ' . TestDatabase::class . '::connection' - ); - - TestDatabase::connection(); - } - - /** - * @return array{ - * 0:RequiresTestDatabaseSettings&IMock, - * 1:ConnectionAdapter&IMock, - * 2:CleanupStrategy&IMock, - * 3:ConnectionAdapterFactory&IMock, - * } - */ - private function defaultMocks() : array { - $cleanupStrategy = Phake::mock(CleanupStrategy::class); - $connectionAdapterFactory = Phake::mock(ConnectionAdapterFactory::class); - $connectionAdapter = Phake::mock(ConnectionAdapter::class); - $requiresTestDatabase = Phake::mock(RequiresTestDatabaseSettings::class); - - Phake::when($requiresTestDatabase)->connectionAdapterFactory()->thenReturn($connectionAdapterFactory); - Phake::when($requiresTestDatabase)->cleanupStrategy()->thenReturn($cleanupStrategy); - Phake::when($connectionAdapterFactory)->createConnectionAdapter()->thenReturn($connectionAdapter); - - return [$requiresTestDatabase, $connectionAdapter, $cleanupStrategy, $connectionAdapterFactory]; - } - - public function testEstablishingConnectionCreatesConnectionAdapterAndConnectsToTestDatabase() : void { - [$requiresTestDatabase, $connectionAdapter] = $this->defaultMocks(); - - $subject = TestDatabase::createFromTestCaseRequiresDatabase(__CLASS__, $requiresTestDatabase); - $subject->establishConnection(); - - Phake::verify($connectionAdapter, Phake::times(1))->establishConnection(); - } - - public function testCallingEstablishingConnectionMultipleTimesWithoutClosingConnectionThrowsException() : void { - [$requiresTestDatabase] = $this->defaultMocks(); - - $subject = TestDatabase::createFromTestCaseRequiresDatabase(__CLASS__, $requiresTestDatabase); - $subject->establishConnection(); - - $this->expectException(ConnectionAlreadyEstablished::class); - $this->expectExceptionMessage( - 'Attempting to establish an already established connection. Please ensure you call ' . TestDatabase::class . '::closeConnection before calling establishConnection again.' - ); - - $subject->establishConnection(); - } - - public function testCallingConnectionAfterEstablishingConnectionReturnsTheCorrectConnectionObject() : void { - [$requiresTestDatabase, $connectionAdapter] = $this->defaultMocks(); - - $connection = new \stdClass(); - Phake::when($connectionAdapter)->underlyingConnection()->thenReturn($connection); - - $subject = TestDatabase::createFromTestCaseRequiresDatabase(__CLASS__, $requiresTestDatabase); - $subject->establishConnection(); - - self::assertSame($connection, TestDatabase::connection()); - } - - public function testPrepareForTestWithoutEstablishingConnectionThrowsException() : void { - [$requiresTestDatabase] = $this->defaultMocks(); - $databaseAwareTest = Phake::mock(DatabaseAwareTest::class); - - $subject = TestDatabase::createFromTestCaseRequiresDatabase(__CLASS__, $requiresTestDatabase); - - $this->expectExceptionMessage(ConnectionNotEstablished::class); - $this->expectExceptionMessage( - 'A connection to the test database MUST be established before invoking ' . TestDatabase::class . '::prepareForTest' - ); - - $subject->prepareForTest($databaseAwareTest); - } - - public function testPrepareForTestWithEstablishedConnectionCallsCorrectOperationsInOrder() : void { - [$requiresTestDatabase, $connectionAdapter, $cleanupStrategy] = $this->defaultMocks(); - $databaseAwareTest = Phake::mock(DatabaseAwareTest::class); - $fixture = Phake::mock(Fixture::class); - Phake::when($databaseAwareTest)->fixtures()->thenReturn([$fixture]); - - $subject = TestDatabase::createFromTestCaseRequiresDatabase(__CLASS__, $requiresTestDatabase); - - $subject->establishConnection(); - $subject->prepareForTest($databaseAwareTest); - - Phake::inOrder( - Phake::verify($connectionAdapter, Phake::times(1))->establishConnection(), - Phake::verify($cleanupStrategy, Phake::times(1))->cleanupBeforeTest($databaseAwareTest, $connectionAdapter), - Phake::verify($connectionAdapter, Phake::times(1))->insert([$fixture]), - ); - } - - public function testCleanupAfterTestWithoutEstablishingConnectionThrowsException() : void { - [$requiresTestDatabase] = $this->defaultMocks(); - $databaseAwareTest = Phake::mock(DatabaseAwareTest::class); - - $subject = TestDatabase::createFromTestCaseRequiresDatabase(__CLASS__, $requiresTestDatabase); - - $this->expectExceptionMessage(ConnectionNotEstablished::class); - $this->expectExceptionMessage( - 'A connection to the test database MUST be established before invoking ' . TestDatabase::class . '::cleanupAfterTest' - ); - - $subject->cleanupAfterTest($databaseAwareTest); - } - - public function testCleanupAfterTestWithEstablishedConnectionsCallsCorrectOperationsInOrder() : void { - [$requiresTestDatabase, $connectionAdapter, $cleanupStrategy] = $this->defaultMocks(); - $databaseAwareTest = Phake::mock(DatabaseAwareTest::class); - $fixture = Phake::mock(Fixture::class); - Phake::when($databaseAwareTest)->fixtures()->thenReturn([$fixture]); - - $subject = TestDatabase::createFromTestCaseRequiresDatabase(__CLASS__, $requiresTestDatabase); - $subject->establishConnection(); - $subject->cleanupAfterTest($databaseAwareTest); - - Phake::inOrder( - Phake::verify($connectionAdapter, Phake::times(1))->establishConnection(), - Phake::verify($cleanupStrategy, Phake::times(1))->teardownAfterTest($databaseAwareTest, $connectionAdapter) - ); - } - - public function testCloseConnectionWithoutEstablishingConnectionThrowsException() : void { - [$requiresTestDatabase] = $this->defaultMocks(); - - $subject = TestDatabase::createFromTestCaseRequiresDatabase(__CLASS__, $requiresTestDatabase); - - $this->expectExceptionMessage(ConnectionNotEstablished::class); - $this->expectExceptionMessage( - 'A connection to the test database MUST be established before invoking ' . TestDatabase::class . '::closeConnection' - ); - - $subject->closeConnection(); - } - - public function testCloseConnectionWithEstablishedConnectionCallsCorrectConnectionAdapterMethodAndNullsTheStaticConnectionAdapter() : void { - [$requiresTestDatabase, $connectionAdapter] = $this->defaultMocks(); - - $subject = TestDatabase::createFromTestCaseRequiresDatabase(__CLASS__, $requiresTestDatabase); - $subject->establishConnection(); - $subject->closeConnection(); - - Phake::inOrder( - Phake::verify($connectionAdapter, Phake::times(1))->establishConnection(), - Phake::verify($connectionAdapter, Phake::times(1))->closeConnection() - ); - - $reflection = new \ReflectionClass(TestDatabase::class); - self::assertNull($reflection->getStaticPropertyValue('connectionAdapter')); - } - - public function testCallingTableWithoutEstablishingConnectionThrowsException() : void { - $this->expectExceptionMessage(ConnectionNotEstablished::class); - $this->expectExceptionMessage( - 'A connection to the test database MUST be established before invoking ' . TestDatabase::class . '::table' - ); - - TestDatabase::table('name'); - } - - public function testCallingTableWithEstablishedConnectionReturnsTableFromConnectionAdapterCall() : void { - [$requiresTestDatabase, $connectionAdapter] = $this->defaultMocks(); - Phake::when($connectionAdapter)->selectAll('table_name')->thenReturn([ - ['id' => 1, 'name' => 'foo'], - ['id' => 2, 'name' => 'bar'], - ['id' => 3, 'name' => 'baz'], - ]); - - $subject = TestDatabase::createFromTestCaseRequiresDatabase(__CLASS__, $requiresTestDatabase); - $subject->establishConnection(); - - $actual = TestDatabase::table('table_name'); - - self::assertSame('table_name', $actual->name()); - self::assertCount(3, $actual); - self::assertSame(1, $actual->row(0)->get('id')); - self::assertSame('foo', $actual->row(0)->get('name')); - self::assertSame(2, $actual->row(1)->get('id')); - self::assertSame('bar', $actual->row(1)->get('name')); - self::assertSame(3, $actual->row(2)->get('id')); - self::assertSame('baz', $actual->row(2)->get('name')); - } - -} \ No newline at end of file