Browse Source

Re-organize fixtures into strategies

Corey Taylor 4 years ago
parent
commit
41dd028e65
26 changed files with 622 additions and 1256 deletions
  1. 0 5
      phpstan-baseline.neon
  2. 0 13
      psalm-baseline.xml
  3. 0 4
      src/Database/ConstraintsInterface.php
  4. 2 0
      src/Database/Statement/StatementDecorator.php
  5. 0 66
      src/TestSuite/Fixture/CollectionResetStrategy.php
  6. 0 293
      src/TestSuite/Fixture/FixtureDataManager.php
  7. 175 0
      src/TestSuite/Fixture/FixtureHelper.php
  8. 0 111
      src/TestSuite/Fixture/FixtureLoader.php
  9. 7 14
      src/TestSuite/Fixture/ResetStrategyInterface.php
  10. 0 2
      src/TestSuite/Fixture/PHPUnitExtension.php
  11. 89 0
      src/TestSuite/Fixture/TransactionFixtureStrategy.php
  12. 0 109
      src/TestSuite/Fixture/TransactionResetStrategy.php
  13. 58 0
      src/TestSuite/Fixture/TruncateFixtureStrategy.php
  14. 0 133
      src/TestSuite/Fixture/TruncationResetStrategy.php
  15. 35 86
      src/TestSuite/TestCase.php
  16. 0 7
      tests/TestCase/Database/QueryTest.php
  17. 2 9
      tests/TestCase/Http/SessionTest.php
  18. 1 1
      tests/TestCase/ORM/RulesCheckerIntegrationTest.php
  19. 0 58
      tests/TestCase/TestSuite/Fixture/CollectionResetStrategyTest.php
  20. 0 214
      tests/TestCase/TestSuite/Fixture/FixtureDataManagerTest.php
  21. 149 0
      tests/TestCase/TestSuite/Fixture/FixtureHelperTest.php
  22. 52 0
      tests/TestCase/TestSuite/Fixture/TransactionFixtureStrategyTest.php
  23. 0 45
      tests/TestCase/TestSuite/Fixture/TransactionResetStrategyTest.php
  24. 52 0
      tests/TestCase/TestSuite/Fixture/TruncateFixtureStrategyTest.php
  25. 0 51
      tests/TestCase/TestSuite/Fixture/TruncationResetStrategyTest.php
  26. 0 35
      tests/TestCase/TestSuite/TestCaseTest.php

+ 0 - 5
phpstan-baseline.neon

@@ -466,11 +466,6 @@ parameters:
 			path: src/TestSuite/Fixture/TestFixture.php
 
 		-
-			message: "#^Constructor of class Cake\\\\TestSuite\\\\Fixture\\\\TransactionResetStrategy has an unused parameter \\$loader\\.$#"
-			count: 1
-			path: src/TestSuite/Fixture/TransactionResetStrategy.php
-
-		-
 			message: "#^Parameter \\#1 \\$separator of function explode expects non\\-empty\\-string, string given\\.$#"
 			count: 1
 			path: src/Utility/Hash.php

+ 0 - 13
psalm-baseline.xml

@@ -222,19 +222,6 @@
       <code>new AssertionFailedError($message)</code>
     </InternalMethod>
   </file>
-  <file src="src/TestSuite/Fixture/FixtureDataManager.php">
-    <DeprecatedProperty occurrences="1">
-      <code>$test-&gt;autoFixtures</code>
-    </DeprecatedProperty>
-  </file>
-  <file src="src/TestSuite/TestCase.php">
-    <DeprecatedProperty occurrences="4">
-      <code>$this-&gt;autoFixtures</code>
-      <code>$this-&gt;autoFixtures</code>
-      <code>$this-&gt;autoFixtures</code>
-      <code>$this-&gt;autoFixtures</code>
-    </DeprecatedProperty>
-  </file>
   <file src="src/TestSuite/TestSuite.php">
     <InternalClass occurrences="1">
       <code>BaseTestSuite</code>

+ 0 - 4
src/Database/ConstraintsInterface.php

@@ -20,10 +20,6 @@ use Cake\Datasource\ConnectionInterface;
 
 /**
  * Defines the interface for a fixture that needs to manage constraints.
- *
- * If an implementation of `Cake\Datasource\FixtureInterface` also implements
- * this interface, the FixtureDataManager will use these methods to manage
- * a fixtures constraints.
  */
 interface ConstraintsInterface
 {

+ 2 - 0
src/Database/Statement/StatementDecorator.php

@@ -21,6 +21,7 @@ use Cake\Database\StatementInterface;
 use Cake\Database\TypeConverterTrait;
 use Countable;
 use IteratorAggregate;
+use ReturnTypeWillChange;
 use RuntimeException;
 
 /**
@@ -285,6 +286,7 @@ class StatementDecorator implements StatementInterface, Countable, IteratorAggre
      * @return \Cake\Database\StatementInterface
      * @psalm-suppress ImplementedReturnTypeMismatch
      */
+    #[ReturnTypeWillChange]
     public function getIterator(): StatementInterface
     {
         if (!$this->_hasExecuted) {

+ 0 - 66
src/TestSuite/Fixture/CollectionResetStrategy.php

@@ -1,66 +0,0 @@
-<?php
-declare(strict_types=1);
-
-/**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link          https://cakephp.org CakePHP(tm) Project
- * @since         4.3.0
- * @license       https://opensource.org/licenses/mit-license.php MIT License
- */
-namespace Cake\TestSuite\Fixture;
-
-/**
- * Fixture state strategy that combines other strategies
- *
- * Useful when your application is using multiple storage
- * systems that require their own state resetting. For example,
- * a relational database and elasticsearch.
- */
-class CollectionResetStrategy implements ResetStrategyInterface
-{
-    /**
-     * @var array<\Cake\TestSuite\Fixture\ResetStrategyInterface>
-     */
-    protected $items = [];
-
-    /**
-     * Constructor
-     *
-     * @param array<\Cake\TestSuite\Fixture\ResetStrategyInterface> $items The strategies to aggregate.
-     */
-    public function __construct(array $items)
-    {
-        $this->items = $items;
-    }
-
-    /**
-     * Call setupTest on each item.
-     *
-     * @return void
-     */
-    public function setupTest(): void
-    {
-        foreach ($this->items as $item) {
-            $item->setupTest();
-        }
-    }
-
-    /**
-     * Call teardownTest on each item.
-     *
-     * @return void
-     */
-    public function teardownTest(): void
-    {
-        foreach ($this->items as $item) {
-            $item->teardownTest();
-        }
-    }
-}

+ 0 - 293
src/TestSuite/Fixture/FixtureDataManager.php

@@ -1,293 +0,0 @@
-<?php
-declare(strict_types=1);
-
-/**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link          https://cakephp.org CakePHP(tm) Project
- * @since         4.3.0
- * @license       https://opensource.org/licenses/mit-license.php MIT License
- */
-namespace Cake\TestSuite\Fixture;
-
-use Cake\Core\Configure;
-use Cake\Core\Exception\CakeException;
-use Cake\Database\Driver\Postgres;
-use Cake\Datasource\ConnectionInterface;
-use Cake\Datasource\ConnectionManager;
-use Cake\TestSuite\TestCase;
-use PDOException;
-use RuntimeException;
-use UnexpectedValueException;
-
-/**
- * Data only fixture manager
- *
- * Part of the data-only fixture system. Data-only fixtures assume that
- * you have created the schema for the testsuite *before* running tests.
- * They also assume that any mutations to your schema are reverted by you.
- *
- * This class implements a common interface with the Cake\TestSuite\FixtureManager so that
- * test cases only need to interact with a single interface.
- */
-class FixtureDataManager extends FixtureLoader
-{
-    /**
-     * A mapping between the fixture name (including the prefix) and the object.
-     *
-     * @var array<\Cake\Datasource\FixtureInterface>
-     */
-    protected $fixtures = [];
-
-    /**
-     * A map between a fixture name without the plugin prefix and the object.
-     *
-     * @var array<\Cake\Datasource\FixtureInterface>
-     */
-    protected $nameMap = [];
-
-    /**
-     * @var array<string>
-     */
-    protected $inserted = [];
-
-    /**
-     * A map of test classes and whether or not their fixtures have
-     * been added to the nameMap.
-     *
-     * @var array<bool>
-     */
-    protected $visitedTests = [];
-
-    /**
-     * Looks for fixture files and instantiates the classes accordingly
-     *
-     * @param \Cake\TestSuite\TestCase $test The test suite to load fixtures for.
-     * @return void
-     * @throws \UnexpectedValueException when a referenced fixture does not exist.
-     */
-    protected function loadFixtureClasses(TestCase $test): void
-    {
-        $fixtures = $test->getFixtures();
-        if (!$fixtures || isset($this->visitedTests[get_class($test)])) {
-            return;
-        }
-        $this->visitedTests[get_class($test)] = true;
-
-        foreach ($fixtures as $fixture) {
-            if (isset($this->fixtures[$fixture])) {
-                continue;
-            }
-
-            if (strpos($fixture, '.')) {
-                [$type, $pathName] = explode('.', $fixture, 2);
-                $path = explode('/', $pathName);
-                $name = array_pop($path);
-                $additionalPath = implode('\\', $path);
-
-                if ($type === 'core') {
-                    $baseNamespace = 'Cake';
-                } elseif ($type === 'app') {
-                    $baseNamespace = Configure::read('App.namespace');
-                } elseif ($type === 'plugin') {
-                    [$plugin, $name] = explode('.', $pathName);
-                    $baseNamespace = str_replace('/', '\\', $plugin);
-                    $additionalPath = null;
-                } else {
-                    $baseNamespace = '';
-                    $name = $fixture;
-                }
-
-                if (strpos($name, '/') > 0) {
-                    $name = str_replace('/', '\\', $name);
-                }
-
-                $nameSegments = [
-                    $baseNamespace,
-                    'Test\Fixture',
-                    $additionalPath,
-                    $name . 'Fixture',
-                ];
-                /** @psalm-var class-string<\Cake\Datasource\FixtureInterface> */
-                $className = implode('\\', array_filter($nameSegments));
-            } else {
-                /** @psalm-var class-string<\Cake\Datasource\FixtureInterface> */
-                $className = $fixture;
-                /** @psalm-suppress PossiblyFalseArgument */
-                $name = preg_replace('/Fixture\z/', '', substr(strrchr($fixture, '\\'), 1));
-            }
-
-            if (class_exists($className)) {
-                $this->fixtures[$fixture] = new $className();
-                $this->nameMap[$name] = $this->fixtures[$fixture];
-            } else {
-                $msg = sprintf(
-                    'Referenced fixture class "%s" not found. Fixture "%s" was referenced in test case "%s".',
-                    $className,
-                    $fixture,
-                    get_class($test)
-                );
-                throw new UnexpectedValueException($msg);
-            }
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function loadSingle(string $name, ?ConnectionInterface $connection = null): void
-    {
-        if (!isset($this->nameMap[$name])) {
-            throw new UnexpectedValueException(sprintf('Referenced fixture class %s not found', $name));
-        }
-
-        $fixture = $this->nameMap[$name];
-        if (!$connection) {
-            $connection = ConnectionManager::get($fixture->connection());
-        }
-
-        $fixture->insert($connection);
-        $this->inserted[] = $fixture->sourceName();
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function load(TestCase $test): void
-    {
-        $fixtures = $test->getFixtures();
-        if (!$fixtures || !$test->autoFixtures) {
-            return;
-        }
-
-        try {
-            $insert = function (ConnectionInterface $db, array $fixtures) use ($test): void {
-                foreach ($fixtures as $fixture) {
-                    try {
-                        $fixture->insert($db);
-                        $this->inserted[] = $fixture->sourceName();
-                    } catch (PDOException $e) {
-                        $msg = sprintf(
-                            'Unable to insert fixture "%s" in "%s" test case: ' . "\n" . '%s',
-                            get_class($fixture),
-                            get_class($test),
-                            $e->getMessage()
-                        );
-                        throw new CakeException($msg, null, $e);
-                    }
-                }
-            };
-            $this->runOperation($fixtures, $insert);
-        } catch (PDOException $e) {
-            $msg = sprintf(
-                'Unable to insert fixtures for "%s" test case. %s',
-                get_class($test),
-                $e->getMessage()
-            );
-            throw new RuntimeException($msg, 0, $e);
-        }
-    }
-
-    /**
-     * Run a function on each connection and collection of fixtures.
-     *
-     * @param array<string> $fixtures A list of fixtures to operate on.
-     * @param callable $operation The operation to run on each connection + fixture set.
-     * @return void
-     */
-    protected function runOperation(array $fixtures, callable $operation): void
-    {
-        $dbs = $this->fixtureConnections($fixtures);
-        foreach ($dbs as $connection => $fixtures) {
-            $db = ConnectionManager::get($connection);
-            if (
-                method_exists($db, 'transactional') &&
-                method_exists($db, 'disableConstraints') &&
-                $db->getDriver() instanceof Postgres
-            ) {
-                // disabling foreign key constraints is only valid in a transaction for postgres
-                $db->transactional(function () use ($db, $fixtures, $operation): void {
-                    $db->disableConstraints(function () use ($db, $fixtures, $operation): void {
-                        $operation($db, $fixtures);
-                    });
-                });
-            } elseif (method_exists($db, 'disableConstraints')) {
-                $db->disableConstraints(function () use ($db, $fixtures, $operation): void {
-                    $operation($db, $fixtures);
-                });
-            } else {
-                $operation($db, $fixtures);
-            }
-        }
-    }
-
-    /**
-     * Get the unique list of connections that a set of fixtures contains.
-     *
-     * @param array<string> $fixtures The array of fixtures a list of connections is needed from.
-     * @return array An array of connection names.
-     */
-    protected function fixtureConnections(array $fixtures): array
-    {
-        $dbs = [];
-        foreach ($fixtures as $name) {
-            if (!empty($this->fixtures[$name])) {
-                $fixture = $this->fixtures[$name];
-                $dbs[$fixture->connection()][$name] = $fixture;
-            }
-        }
-
-        return $dbs;
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function setupTest(TestCase $test): void
-    {
-        $stateReset = $test->getResetStrategy();
-        $stateReset->setupTest();
-
-        $this->inserted = [];
-        if (!$test->getFixtures()) {
-            return;
-        }
-        $this->loadFixtureClasses($test);
-        $this->load($test);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function teardownTest(TestCase $test): void
-    {
-        if (!$test->getFixtures()) {
-            return;
-        }
-
-        $stateReset = $test->getResetStrategy();
-        $stateReset->teardownTest();
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function loaded(): array
-    {
-        return $this->fixtures;
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function getInserted(): array
-    {
-        return $this->inserted;
-    }
-}

+ 175 - 0
src/TestSuite/Fixture/FixtureHelper.php

@@ -0,0 +1,175 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         5.0.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\TestSuite\Fixture;
+
+use Cake\Core\Configure;
+use Cake\Database\Driver\Postgres;
+use Cake\Datasource\ConnectionInterface;
+use Cake\Datasource\ConnectionManager;
+use Closure;
+use UnexpectedValueException;
+
+/**
+ * Helper for managing fixtures.
+ */
+class FixtureHelper
+{
+    /**
+     * Finds fixtures from their TestCase names such as 'core.Articles'.
+     *
+     * @param array<string> $fixtureNames Fixture names from test case
+     * @return array<\Cake\Datasource\FixtureInterface>
+     */
+    public function loadFixtures(array $fixtureNames): array
+    {
+        $fixtures = [];
+        foreach ($fixtureNames as $fixtureName) {
+            if (strpos($fixtureName, '.')) {
+                [$type, $pathName] = explode('.', $fixtureName, 2);
+                $path = explode('/', $pathName);
+                $name = array_pop($path);
+                $additionalPath = implode('\\', $path);
+
+                if ($type === 'core') {
+                    $baseNamespace = 'Cake';
+                } elseif ($type === 'app') {
+                    $baseNamespace = Configure::read('App.namespace');
+                } elseif ($type === 'plugin') {
+                    [$plugin, $name] = explode('.', $pathName);
+                    $baseNamespace = str_replace('/', '\\', $plugin);
+                    $additionalPath = null;
+                } else {
+                    $baseNamespace = '';
+                    $name = $fixtureName;
+                }
+
+                if (strpos($name, '/') > 0) {
+                    $name = str_replace('/', '\\', $name);
+                }
+
+                $nameSegments = [
+                    $baseNamespace,
+                    'Test\Fixture',
+                    $additionalPath,
+                    $name . 'Fixture',
+                ];
+                /** @psalm-var class-string<\Cake\Datasource\FixtureInterface> */
+                $className = implode('\\', array_filter($nameSegments));
+            } else {
+                /** @psalm-var class-string<\Cake\Datasource\FixtureInterface> */
+                $className = $fixtureName;
+                /** @psalm-suppress PossiblyFalseArgument */
+                $name = preg_replace('/Fixture\z/', '', substr(strrchr($fixtureName, '\\'), 1));
+            }
+
+            if (isset($fixtures[$className])) {
+                throw new UnexpectedValueException("Found duplicate fixture `$fixtureName`.");
+            }
+
+            if (!class_exists($className)) {
+                throw new UnexpectedValueException("Could not find fixture `$fixtureName`.");
+            }
+
+            $fixtures[$className] = new $className();
+        }
+
+        return $fixtures;
+    }
+
+    /**
+     * Runs the callback once per connection.
+     *
+     * The callback signature:
+     * ```
+     * function callback(ConnectionInterface $connection, array $fixtures)
+     * ```
+     *
+     * @param \Closure $callback Callback run per connection
+     * @param array<\Cake\Datasource\FixtureInterface> $fixtures Test fixtures
+     * @return void
+     */
+    public function runPerConnection(Closure $callback, array $fixtures): void
+    {
+        $groups = [];
+        foreach ($fixtures as $fixture) {
+            $groups[$fixture->connection()][] = $fixture;
+        }
+
+        foreach ($groups as $connectionName => $fixtures) {
+            $callback(ConnectionManager::get($connectionName), $fixtures);
+        }
+    }
+
+    /**
+     * Inserts fixture data.
+     *
+     * @param array<\Cake\Datasource\FixtureInterface> $fixtures Test fixtures
+     * @return void
+     */
+    public function insert(array $fixtures): void
+    {
+        $this->runPerConnection(function (ConnectionInterface $connection, array $groupFixtures) {
+            if (
+                method_exists($connection, 'transactional') &&
+                method_exists($connection, 'disableConstraints') &&
+                $connection->getDriver() instanceof Postgres
+            ) {
+                // disabling foreign key constraints is only valid in a transaction
+                $connection->transactional(function () use ($connection, $groupFixtures) {
+                    $connection->disableConstraints(function () use ($connection, $groupFixtures) {
+                        foreach ($groupFixtures as $fixture) {
+                            $fixture->insert($connection);
+                        }
+                    });
+                });
+            } elseif (method_exists($connection, 'disableConstraints')) {
+                $connection->disableConstraints(function () use ($connection, $groupFixtures) {
+                    foreach ($groupFixtures as $fixture) {
+                        $fixture->insert($connection);
+                    }
+                });
+            } else {
+                foreach ($groupFixtures as $fixture) {
+                    $fixture->insert($connection);
+                }
+            }
+        }, $fixtures);
+    }
+
+    /**
+     * Truncates fixture tables.
+     *
+     * @param array<\Cake\Datasource\FixtureInterface> $fixtures Test fixtures
+     * @return void
+     */
+    public function truncate(array $fixtures): void
+    {
+        $this->runPerConnection(function (ConnectionInterface $connection, array $groupFixtures) {
+            if (method_exists($connection, 'disableConstraints')) {
+                $connection->disableConstraints(function () use ($connection, $groupFixtures) {
+                    foreach ($groupFixtures as $fixture) {
+                        $fixture->truncate($connection);
+                    }
+                });
+            } else {
+                foreach ($groupFixtures as $fixture) {
+                    $fixture->truncate($connection);
+                }
+            }
+        }, $fixtures);
+    }
+}

+ 0 - 111
src/TestSuite/Fixture/FixtureLoader.php

@@ -1,111 +0,0 @@
-<?php
-declare(strict_types=1);
-
-/**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link          https://cakephp.org CakePHP(tm) Project
- * @since         4.3.0
- * @license       https://opensource.org/licenses/mit-license.php MIT License
- */
-namespace Cake\TestSuite\Fixture;
-
-use Cake\Datasource\ConnectionInterface;
-use Cake\TestSuite\TestCase;
-
-/**
- * Abstract base class and singleton container for fixture
- * managers.
- */
-abstract class FixtureLoader
-{
-    /**
-     * @var \Cake\TestSuite\Fixture\FixtureLoader|null
-     */
-    private static $instance;
-
-    /**
-     * Set the shared instance
-     *
-     * @param \Cake\TestSuite\Fixture\FixtureLoader $instance The instance to set.
-     * @return void
-     */
-    public static function setInstance(FixtureLoader $instance): void
-    {
-        static::$instance = $instance;
-    }
-
-    /**
-     * Get the shared instance
-     *
-     * @return \Cake\TestSuite\Fixture\FixtureLoader|null
-     */
-    public static function getInstance(): ?self
-    {
-        return static::$instance;
-    }
-
-    /**
-     * Loads the data for a single fixture.
-     *
-     * @param string $name of the fixture
-     * @param \Cake\Datasource\ConnectionInterface|null $connection Connection instance or null
-     *  to get a Connection from the fixture.
-     * @return void
-     * @throws \UnexpectedValueException if $name is not a previously fixtures class
-     */
-    abstract public function loadSingle(
-        string $name,
-        ?ConnectionInterface $connection = null
-    ): void;
-
-    /**
-     * Creates records defined in a test case's fixtures.
-     *
-     * @param \Cake\TestSuite\TestCase $test The test to inspect for fixture loading.
-     * @return void
-     * @throws \Cake\Core\Exception\CakeException When fixture records cannot be inserted.
-     * @throws \RuntimeException
-     */
-    abstract public function load(TestCase $test): void;
-
-    /**
-     * Setup fixtures for the provided test.
-     *
-     * Called by TestCase during its setUp() method.
-     *
-     * @param \Cake\TestSuite\TestCase $test The test to inspect for fixture loading.
-     * @return void
-     */
-    abstract public function setupTest(TestCase $test): void;
-
-    /**
-     * Teardown fixtures for the provided test.
-     *
-     * Called by TestCase during its tearDown() method.
-     *
-     * @param \Cake\TestSuite\TestCase $test The test to inspect for fixture loading.
-     * @return void
-     */
-    abstract public function teardownTest(TestCase $test): void;
-
-    /**
-     * Get the list of all fixtures that have been loaded.
-     *
-     * @return array<\Cake\Datasource\FixtureInterface>
-     */
-    abstract public function loaded(): array;
-
-    /**
-     * Get the list of fixture tables that were loaded since the last call to setupTest.
-     *
-     * @return array<string>
-     */
-    abstract public function getInserted(): array;
-}

+ 7 - 14
src/TestSuite/Fixture/ResetStrategyInterface.php

@@ -11,33 +11,26 @@ declare(strict_types=1);
  *
  * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  * @link          https://cakephp.org CakePHP(tm) Project
- * @since         4.3.0
+ * @since         5.0.0
  * @license       https://opensource.org/licenses/mit-license.php MIT License
  */
 namespace Cake\TestSuite\Fixture;
 
 /**
- * Interface for fixture state management.
- *
- * This interface exposes hooks that run before and
- * after tests. This allows test database state to be 'prepared'
- * and 'cleaned up'.
+ * Base interface for strategies used to manage fixtures for TestCase.
  */
-interface ResetStrategyInterface
+interface FixtureStrategyInterface
 {
     /**
-     * Before test hook
-     *
-     * Fired before each test is started.
+     * Called before each test run in each TestCase.
      *
+     * @param array<string> $fixtureNames Name of fixtures used by test.
      * @return void
      */
-    public function setupTest(): void;
+    public function setupTest(array $fixtureNames): void;
 
     /**
-     * After test hook
-     *
-     * Fired after each test is complete.
+     * Called after each test run in each TestCase.
      *
      * @return void
      */

+ 0 - 2
src/TestSuite/Fixture/PHPUnitExtension.php

@@ -31,8 +31,6 @@ class PHPUnitExtension implements BeforeFirstTestHook
      */
     public function __construct()
     {
-        FixtureLoader::setInstance(new FixtureDataManager());
-
         $enableLogging = in_array('--debug', $_SERVER['argv'] ?? [], true);
         $this->aliasConnections($enableLogging);
         if ($enableLogging) {

+ 89 - 0
src/TestSuite/Fixture/TransactionFixtureStrategy.php

@@ -0,0 +1,89 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         5.0.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\TestSuite\Fixture;
+
+use Cake\Database\Connection;
+use RuntimeException;
+
+/**
+ * Fixture strategy that wraps fixtures in a transaction that is rolled back
+ * after each test.
+ *
+ * Any test that calls Connection::rollback(true) will break this strategy.
+ */
+class TransactionFixtureStrategy implements FixtureStrategyInterface
+{
+    /**
+     * @var \Cake\TestSuite\Fixture\FixtureHelper
+     */
+    protected $helper;
+
+    /**
+     * @var array<\Cake\Datasource\FixtureInterface>
+     */
+    protected $fixtures = [];
+
+    /**
+     * Initialize strategy.
+     */
+    public function __construct()
+    {
+        $this->helper = new FixtureHelper();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function setupTest(array $fixtureNames): void
+    {
+        $this->fixtures = $this->helper->loadFixtures($fixtureNames);
+
+        $this->helper->runPerConnection(function ($connection) {
+            if ($connection instanceof Connection) {
+                assert(
+                    $connection->inTransaction() === false,
+                    'Cannot start transaction strategy inside a transaction. This is most likely a bug.'
+                );
+                $connection->enableSavePoints();
+                if (!$connection->isSavePointsEnabled()) {
+                    throw new RuntimeException(
+                        "Could not enable save points for the `{$connection->configName()}` connection. " .
+                            'Your database needs to support savepoints in order to use ' .
+                            'TransactionFixtureStrategy.'
+                    );
+                }
+
+                $connection->begin();
+                $connection->createSavePoint('__fixtures__');
+            }
+        }, $this->fixtures);
+
+        $this->helper->insert($this->fixtures);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function teardownTest(): void
+    {
+        $this->helper->runPerConnection(function ($connection) {
+            if ($connection->inTransaction()) {
+                $connection->rollback(true);
+            }
+        }, $this->fixtures);
+    }
+}

+ 0 - 109
src/TestSuite/Fixture/TransactionResetStrategy.php

@@ -1,109 +0,0 @@
-<?php
-declare(strict_types=1);
-
-/**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link          https://cakephp.org CakePHP(tm) Project
- * @since         4.3.0
- * @license       https://opensource.org/licenses/mit-license.php MIT License
- */
-namespace Cake\TestSuite\Fixture;
-
-use Cake\Database\Connection;
-use Cake\Datasource\ConnectionManager;
-use RuntimeException;
-
-/**
- * Fixture state strategy that wraps tests in transactions
- * that are rolled back at the end of the transaction.
- *
- * This strategy aims to gives good performance at the cost
- * of not being able to query data in fixtures from another
- * process.
- */
-class TransactionResetStrategy implements ResetStrategyInterface
-{
-    /**
-     * Constructor.
-     *
-     * @param \Cake\TestSuite\Fixture\FixtureLoader $loader The fixture loader being used.
-     * @return void
-     */
-    public function __construct(FixtureLoader $loader)
-    {
-        $this->checkConnections();
-    }
-
-    /**
-     * Ensure that all `Connection` connections support savepoints.
-     *
-     * @return void
-     */
-    protected function checkConnections(): void
-    {
-        $connections = ConnectionManager::configured();
-        foreach ($connections as $name) {
-            $connection = ConnectionManager::get($name);
-            if ($connection instanceof Connection) {
-                $connection->enableSavePoints();
-                if (!$connection->isSavePointsEnabled()) {
-                    throw new RuntimeException(
-                        "Could not enable save points for the `{$name}` connection. " .
-                            'Your database needs to support savepoints in order to use the ' .
-                            'TransactionResetStrategy for fixtures.'
-                    );
-                }
-            }
-        }
-    }
-
-    /**
-     * Before each test start a transaction.
-     *
-     * @return void
-     */
-    public function setupTest(): void
-    {
-        $connections = ConnectionManager::configured();
-        foreach ($connections as $connection) {
-            if (strpos($connection, 'test') !== 0) {
-                continue;
-            }
-            $db = ConnectionManager::get($connection);
-            if ($db instanceof Connection) {
-                $db->begin();
-                $db->createSavePoint('__fixtures__');
-            }
-        }
-    }
-
-    /**
-     * After each test rollback the transaction.
-     *
-     * As long as the application code has balanced BEGIN/ROLLBACK
-     * operations we should end up at a transaction depth of 0
-     * and we will rollback the root transaction started in beforeTest()
-     *
-     * @return void
-     */
-    public function teardownTest(): void
-    {
-        $connections = ConnectionManager::configured();
-        foreach ($connections as $connection) {
-            if (strpos($connection, 'test') !== 0) {
-                continue;
-            }
-            $db = ConnectionManager::get($connection);
-            if ($db instanceof Connection) {
-                $db->rollback(true);
-            }
-        }
-    }
-}

+ 58 - 0
src/TestSuite/Fixture/TruncateFixtureStrategy.php

@@ -0,0 +1,58 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         5.0.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\TestSuite\Fixture;
+
+/**
+ * Fixture strategy that truncates all fixture ables at the end of test.
+ */
+class TruncateFixtureStrategy implements FixtureStrategyInterface
+{
+    /**
+     * @var \Cake\TestSuite\Fixture\FixtureHelper
+     */
+    protected $helper;
+
+    /**
+     * @var array<\Cake\Datasource\FixtureInterface>
+     */
+    protected $fixtures = [];
+
+    /**
+     * Initialize strategy.
+     */
+    public function __construct()
+    {
+        $this->helper = new FixtureHelper();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function setupTest(array $fixtureNames): void
+    {
+        $this->fixtures = $this->helper->loadFixtures($fixtureNames);
+        $this->helper->insert($this->fixtures);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function teardownTest(): void
+    {
+        $this->helper->truncate($this->fixtures);
+    }
+}

+ 0 - 133
src/TestSuite/Fixture/TruncationResetStrategy.php

@@ -1,133 +0,0 @@
-<?php
-declare(strict_types=1);
-
-/**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link          https://cakephp.org CakePHP(tm) Project
- * @since         4.3.0
- * @license       https://opensource.org/licenses/mit-license.php MIT License
- */
-namespace Cake\TestSuite\Fixture;
-
-use Cake\Database\Connection;
-use Cake\Database\Schema\SqlGeneratorInterface;
-use Cake\Database\Schema\TableSchemaInterface;
-use Cake\Datasource\ConnectionManager;
-
-/**
- * Fixture state strategy that truncates tables before a test.
- *
- * This strategy is slower than the TransactionResetStrategy, but
- * allows tests to reset state when applications require operations
- * that cannot be executed in a transaction. An example
- * of this is DDL operations in MySQL which auto-commit any open
- * transactions.
- *
- * This mode also offers 'backwards compatible' behavior
- * with the schema + data fixture system. Only tables that have
- * fixture data 'loaded' will be truncated.
- */
-class TruncationResetStrategy implements ResetStrategyInterface
-{
-    /**
-     * A map of connections to the tables they contain.
-     * Caching schema descriptions helps improve performance and
-     * is required for SQLServer to reset sequences.
-     *
-     * @var array
-     */
-    protected static $tables = [];
-
-    /**
-     * @var \Cake\TestSuite\Fixture\FixtureLoader
-     */
-    protected $loader;
-
-    /**
-     * Constructor.
-     *
-     * @param \Cake\TestSuite\Fixture\FixtureLoader $loader The fixture loader being used.
-     * @return void
-     */
-    public function __construct(FixtureLoader $loader)
-    {
-        $this->loader = $loader;
-    }
-
-    /**
-     * Before each test start a transaction.
-     *
-     * @return void
-     */
-    public function teardownTest(): void
-    {
-        $connections = ConnectionManager::configured();
-        foreach ($connections as $connection) {
-            if (strpos($connection, 'test') !== 0) {
-                continue;
-            }
-            $db = ConnectionManager::get($connection);
-            if (!($db instanceof Connection)) {
-                continue;
-            }
-            $inserted = $this->loader->getInserted();
-            if (empty($inserted)) {
-                continue;
-            }
-            $schema = $db->getSchemaCollection();
-            $tables = $schema->listTables();
-            $tables = array_intersect($tables, $inserted);
-
-            $db->disableConstraints(function (Connection $db) use ($tables): void {
-                foreach ($tables as $table) {
-                    $tableSchema = $this->getTableSchema($db, $table);
-                    if ($tableSchema instanceof SqlGeneratorInterface) {
-                        $sql = $tableSchema->truncateSql($db);
-                        foreach ($sql as $stmt) {
-                            $db->execute($stmt)->closeCursor();
-                        }
-                    }
-                }
-            });
-        }
-    }
-
-    /**
-     * No op. Implemented because of interface.
-     *
-     * @return void
-     */
-    public function setupTest(): void
-    {
-    }
-
-    /**
-     * Get the schema description for a table.
-     *
-     * Lazily populates with fixtures as they are used to reduce
-     * the number of reflection queries we use.
-     *
-     * @param \Cake\Database\Connection $db The connection to use.
-     * @param string $table The table to reflect.
-     * @return \Cake\Database\Schema\TableSchemaInterface
-     */
-    protected function getTableSchema(Connection $db, string $table): TableSchemaInterface
-    {
-        $name = $db->configName();
-        if (isset(static::$tables[$name][$table])) {
-            return static::$tables[$name][$table];
-        }
-        $schema = $db->getSchemaCollection();
-        $tableSchema = $schema->describe($table);
-        static::$tables[$name][$table] = $tableSchema;
-
-        return $tableSchema;
-    }
-}

+ 35 - 86
src/TestSuite/TestCase.php

@@ -28,9 +28,8 @@ use Cake\ORM\Table;
 use Cake\Routing\Router;
 use Cake\TestSuite\Constraint\EventFired;
 use Cake\TestSuite\Constraint\EventFiredWith;
-use Cake\TestSuite\Fixture\FixtureLoader;
-use Cake\TestSuite\Fixture\ResetStrategyInterface;
-use Cake\TestSuite\Fixture\TruncationResetStrategy;
+use Cake\TestSuite\Fixture\FixtureStrategyInterface;
+use Cake\TestSuite\Fixture\TruncateFixtureStrategy;
 use Cake\Utility\Inflector;
 use LogicException;
 use PHPUnit\Framework\Constraint\DirectoryExists;
@@ -41,7 +40,6 @@ use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase as BaseTestCase;
 use ReflectionClass;
 use ReflectionException;
-use RuntimeException;
 
 /**
  * Cake TestCase class
@@ -51,13 +49,6 @@ abstract class TestCase extends BaseTestCase
     use LocatorAwareTrait;
 
     /**
-     * The class responsible for managing the creation, loading and removing of fixtures
-     *
-     * @var \Cake\TestSuite\Fixture\FixtureLoader|null
-     */
-    public $fixtureManager;
-
-    /**
      * Fixtures used by this test case.
      *
      * @var array<string>
@@ -65,24 +56,9 @@ abstract class TestCase extends BaseTestCase
     protected $fixtures = [];
 
     /**
-     * By default, all fixtures attached to this class will be truncated and reloaded after each test.
-     * Set this to false to handle manually
-     *
-     * @var bool
-     * @deprecated 4.3.0 autoFixtures is only used by deprecated fixture features.
-     *   This property will be removed in 5.0
+     * @var \Cake\TestSuite\Fixture\FixtureStrategyInterface|null
      */
-    public $autoFixtures = true;
-
-    /**
-     * The classname for the fixture state reset strategy.
-     *
-     * If null, TruncationResetStrategy will be used.
-     *
-     * @var string|null
-     * @psalm-var class-string|null
-     */
-    protected $stateResetStrategy;
+    protected ?FixtureStrategyInterface $fixtureStrategy = null;
 
     /**
      * Configure values to restore at end of test.
@@ -218,10 +194,7 @@ abstract class TestCase extends BaseTestCase
     protected function setUp(): void
     {
         parent::setUp();
-        $this->fixtureManager = FixtureLoader::getInstance();
-        if ($this->fixtureManager) {
-            $this->fixtureManager->setupTest($this);
-        }
+        $this->setupFixtures();
 
         if (!$this->_configure) {
             $this->_configure = Configure::read();
@@ -241,6 +214,8 @@ abstract class TestCase extends BaseTestCase
     protected function tearDown(): void
     {
         parent::tearDown();
+        $this->teardownFixtures();
+
         if ($this->_configure) {
             Configure::clear();
             Configure::write($this->_configure);
@@ -248,46 +223,48 @@ abstract class TestCase extends BaseTestCase
         $this->getTableLocator()->clear();
         $this->_configure = [];
         $this->_tableLocator = null;
-
-        if ($this->fixtureManager) {
-            $this->fixtureManager->teardownTest($this);
-            $this->fixtureManager = null;
-        }
     }
 
     /**
-     * Chooses which fixtures to load for a given test
-     *
-     * Each parameter is a model name that corresponds to a fixture, i.e. 'Posts', 'Authors', etc.
-     * Passing no parameters will cause all fixtures on the test case to load.
+     * Initialized and loads any use fixtures.
      *
      * @return void
-     * @see \Cake\TestSuite\TestCase::$autoFixtures
-     * @throws \RuntimeException when no fixture manager is available.
      */
-    public function loadFixtures(): void
+    protected function setupFixtures(): void
     {
-        if ($this->autoFixtures) {
-            throw new RuntimeException('Cannot use `loadFixtures()` with `$autoFixtures` enabled.');
-        }
-        if ($this->fixtureManager === null) {
-            throw new RuntimeException('No fixture manager to load the test fixture');
+        $fixtureNames = $this->getFixtures();
+        if (empty($fixtureNames)) {
+            return;
         }
 
-        $args = func_get_args();
-        foreach ($args as $class) {
-            $this->fixtureManager->loadSingle($class, null);
-        }
+        $this->fixtureStrategy = $this->getFixtureStrategy();
+        $this->fixtureStrategy->setupTest($fixtureNames);
+    }
 
-        if (empty($args)) {
-            $autoFixtures = $this->autoFixtures;
-            $this->autoFixtures = true;
-            $this->fixtureManager->load($this);
-            $this->autoFixtures = $autoFixtures;
+    /**
+     * Unloads any use fixtures.
+     *
+     * @return void
+     */
+    protected function teardownFixtures(): void
+    {
+        if ($this->fixtureStrategy) {
+            $this->fixtureStrategy->teardownTest();
+            $this->fixtureStrategy = null;
         }
     }
 
     /**
+     * Returns fixture strategy used by these tests.
+     *
+     * @return \Cake\TestSuite\Fixture\FixtureStrategyInterface
+     */
+    protected function getFixtureStrategy(): FixtureStrategyInterface
+    {
+        return new TruncateFixtureStrategy();
+    }
+
+    /**
      * Load routes for the application.
      *
      * If no application class can be found an exception will be raised.
@@ -1040,32 +1017,4 @@ abstract class TestCase extends BaseTestCase
     {
         return $this->fixtures;
     }
-
-    /**
-     * Get the strategy used to reset fixture state.
-     *
-     * Offers basic class construction, if you need more complex
-     * state manager configuration, or multiple state managers,
-     * override this method.
-     *
-     * @return \Cake\TestSuite\Fixture\ResetStrategyInterface
-     */
-    public function getResetStrategy(): ResetStrategyInterface
-    {
-        $strategyClass = $this->stateResetStrategy ?? TruncationResetStrategy::class;
-        try {
-            $reflect = new ReflectionClass($strategyClass);
-        } catch (ReflectionException $e) {
-            throw new RuntimeException("Cannot find class `{$strategyClass}`");
-        }
-        $interface = ResetStrategyInterface::class;
-        if (!$reflect->implementsInterface($interface)) {
-            throw new RuntimeException(
-                "The `{$strategyClass}` does not implement the required `{$interface}` interface."
-            );
-        }
-
-        /** @var \Cake\TestSuite\Fixture\ResetStrategyInterface */
-        return $reflect->newInstance($this->fixtureManager);
-    }
 }

+ 0 - 7
tests/TestCase/Database/QueryTest.php

@@ -3521,10 +3521,6 @@ class QueryTest extends TestCase
             'This test fails sporadically in SQLServer'
         );
 
-        // Load with force dropping tables to avoid identities not being reset properly
-        // in SQL Server when reseeding is applied directly after table creation.
-        $this->fixtureManager->loadSingle('Profiles', null, true);
-
         $profiles = $this->getTableLocator()->get('Profiles');
 
         $query = $profiles
@@ -3565,9 +3561,6 @@ class QueryTest extends TestCase
             $this->connection->getDriver() instanceof Sqlserver,
             'This test fails sporadically in SQLServer'
         );
-        // Load with force dropping tables to avoid identities not being reset properly
-        // in SQL Server when reseeding is applied directly after table creation.
-        $this->fixtureManager->loadSingle('Profiles', null, true);
 
         $profiles = $this->getTableLocator()->get('Profiles');
 

+ 2 - 9
tests/TestCase/Http/SessionTest.php

@@ -29,20 +29,13 @@ use TestApp\Http\Session\TestWebSession;
 class SessionTest extends TestCase
 {
     /**
-     * Fixtures used in the SessionTest
-     *
-     * @var array
-     */
-    protected $fixtures = ['core.CakeSessions', 'core.Sessions'];
-
-    /**
      * tearDown method
      */
-    public function tearDown(): void
+    protected function tearDown(): void
     {
-        unset($_SESSION);
         parent::tearDown();
         $this->clearPlugins();
+        unset($_SESSION);
     }
 
     /**

+ 1 - 1
tests/TestCase/ORM/RulesCheckerIntegrationTest.php

@@ -43,7 +43,7 @@ class RulesCheckerIntegrationTest extends TestCase
     protected $fixtures = [
         'core.Articles', 'core.Tags', 'core.ArticlesTags', 'core.Authors', 'core.Comments',
         'core.SpecialTags', 'core.Categories', 'core.SiteArticles', 'core.SiteAuthors',
-        'core.Comments', 'core.UniqueAuthors',
+        'core.UniqueAuthors',
     ];
 
     /**

+ 0 - 58
tests/TestCase/TestSuite/Fixture/CollectionResetStrategyTest.php

@@ -1,58 +0,0 @@
-<?php
-declare(strict_types=1);
-
-/**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link          https://cakephp.org CakePHP(tm) Project
- * @since         4.3.0
- * @license       https://opensource.org/licenses/mit-license.php MIT License
- */
-namespace Cake\Test\TestCase\TestSuite;
-
-use Cake\TestSuite\Fixture\CollectionResetStrategy;
-use Cake\TestSuite\Fixture\ResetStrategyInterface;
-use Cake\TestSuite\TestCase;
-
-class CollectionResetStrategyTest extends TestCase
-{
-    /**
-     * Test that setupTest calls items.
-     */
-    public function testSetupTest(): void
-    {
-        $one = $this->getMockBuilder(ResetStrategyInterface::class)->getMock();
-        $two = $this->getMockBuilder(ResetStrategyInterface::class)->getMock();
-
-        $one->expects($this->once())->method('setupTest');
-        $two->expects($this->once())->method('setupTest');
-
-        $one->expects($this->never())->method('teardownTest');
-
-        $strategy = new CollectionResetStrategy([$one, $two]);
-        $strategy->setupTest();
-    }
-
-    /**
-     * Test that teardownTest calls items.
-     */
-    public function testTeardownTest(): void
-    {
-        $one = $this->getMockBuilder(ResetStrategyInterface::class)->getMock();
-        $two = $this->getMockBuilder(ResetStrategyInterface::class)->getMock();
-
-        $one->expects($this->once())->method('teardownTest');
-        $two->expects($this->once())->method('teardownTest');
-
-        $one->expects($this->never())->method('setupTest');
-
-        $strategy = new CollectionResetStrategy([$one, $two]);
-        $strategy->teardownTest();
-    }
-}

+ 0 - 214
tests/TestCase/TestSuite/Fixture/FixtureDataManagerTest.php

@@ -1,214 +0,0 @@
-<?php
-declare(strict_types=1);
-
-/**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link          https://cakephp.org CakePHP(tm) Project
- * @since         4.3.0
- * @license       https://opensource.org/licenses/mit-license.php MIT License
- */
-namespace Cake\Test\TestCase\TestSuite\Fixture;
-
-use Cake\Datasource\ConnectionManager;
-use Cake\Datasource\FixtureInterface;
-use Cake\TestSuite\Fixture\FixtureDataManager;
-use Cake\TestSuite\TestCase;
-use UnexpectedValueException;
-
-class FixtureDataManagerTest extends TestCase
-{
-    /**
-     * Schema fixtures to load.
-     *
-     * @var string[]
-     */
-    public $fixtures = ['core.Articles', 'core.Comments'];
-
-    /**
-     * setup
-     */
-    public function setUp(): void
-    {
-        parent::setUp();
-        // Clear fixture tables so we can test insertions.
-        $db = ConnectionManager::get('test');
-        $query = $db->newQuery()
-            ->delete()
-            ->from('comments')
-            ->where('1=1');
-        $query->execute()->closeCursor();
-
-        $query = $db->newQuery()
-            ->delete()
-            ->from('articles')
-            ->where('1=1');
-        $query->execute()->closeCursor();
-    }
-
-    /**
-     * Data provider for valid fixture names.
-     *
-     * @return array
-     */
-    public function invalidProvider(): array
-    {
-        return [
-            ['core.Nope'],
-            ['app.Nope'],
-            ['plugin.NotThere.Nope'],
-        ];
-    }
-
-    /**
-     * Test that setupTest() errors on missing fixture
-     *
-     * @dataProvider invalidProvider
-     * @param string $name Fixture name
-     */
-    public function testSetupTestErrorOnUnknown($name): void
-    {
-        $manager = new FixtureDataManager();
-        $this->fixtures = [$name];
-
-        $this->expectException(UnexpectedValueException::class);
-        $this->expectExceptionMessage('Referenced fixture class');
-        $manager->setupTest($this);
-    }
-
-    /**
-     * Data provider for valid fixture names.
-     *
-     * @return array
-     */
-    public function validProvider(): array
-    {
-        return [
-            ['core.Articles'],
-            ['plugin.TestPlugin.Articles'],
-            ['plugin.Company/TestPluginThree.Articles'],
-        ];
-    }
-
-    /**
-     * Test that setupTest() loads fixtures.
-     *
-     * @dataProvider validProvider
-     * @param string $name The fixture name
-     */
-    public function testSetupTestLoads($name): void
-    {
-        $this->setAppNamespace();
-        // Also loads TestPlugin
-        $this->loadPlugins(['Company/TestPluginThree']);
-
-        $manager = new FixtureDataManager();
-        $this->fixtures = [$name];
-        $manager->setupTest($this);
-
-        $fixtures = $manager->loaded();
-        $this->assertCount(1, $fixtures);
-        $this->assertInstanceOf(FixtureInterface::class, $fixtures[$name]);
-    }
-
-    /**
-     * Test that setupTest() loads fixtures.
-     */
-    public function testSetupTestLoadsMultipleFixtures(): void
-    {
-        $manager = new FixtureDataManager();
-        $this->autoFixtures = false;
-        $manager->setupTest($this);
-
-        $fixtures = $manager->loaded();
-        $this->assertCount(2, $fixtures);
-        $this->assertInstanceOf(FixtureInterface::class, $fixtures['core.Articles']);
-        $this->assertInstanceOf(FixtureInterface::class, $fixtures['core.Comments']);
-    }
-
-    /**
-     * loadSingle on a known fixture.
-     */
-    public function testLoadSingleValid(): void
-    {
-        $manager = new FixtureDataManager();
-        $this->autoFixtures = false;
-        $manager->setupTest($this);
-
-        $manager->loadSingle('Articles');
-        $db = ConnectionManager::get('test');
-        $stmt = $db->newQuery()->select(['count(*)'])->from('articles')->execute();
-        $result = $stmt->fetch()[0];
-        $stmt->closeCursor();
-
-        $this->assertEquals(3, $result);
-    }
-
-    /**
-     * loadSingle on a unknown fixture.
-     */
-    public function testLoadSingleInvalid(): void
-    {
-        $manager = new FixtureDataManager();
-        $this->autoFixtures = false;
-        $manager->setupTest($this);
-
-        $this->expectException(UnexpectedValueException::class);
-        $manager->loadSingle('Nope');
-    }
-
-    /**
-     * Test load() via setupTest()
-     */
-    public function testLoad(): void
-    {
-        $manager = new FixtureDataManager();
-        $manager->setupTest($this);
-
-        $db = ConnectionManager::get('test');
-        $stmt = $db->newQuery()->select(['count(*)'])->from('articles')->execute();
-        $result = $stmt->fetch()[0];
-        $stmt->closeCursor();
-        $this->assertEquals(3, $result);
-
-        $stmt = $db->newQuery()->select(['count(*)'])->from('comments')->execute();
-        $result = $stmt->fetch()[0];
-        $stmt->closeCursor();
-        $this->assertEquals(6, $result);
-    }
-
-    /**
-     * Test getInserted()
-     */
-    public function testGetInserted(): void
-    {
-        $manager = new FixtureDataManager();
-        $manager->setupTest($this);
-
-        $results = $manager->getInserted();
-        $this->assertEquals(['articles', 'comments'], $results);
-    }
-
-    /**
-     * Test getInserted() with autoFixtures
-     */
-    public function testGetInsertedAutofixtures(): void
-    {
-        $manager = new FixtureDataManager();
-        $this->autoFixtures = false;
-        $manager->setupTest($this);
-
-        $results = $manager->getInserted();
-        $this->assertEquals([], $results);
-
-        $manager->loadSingle('Articles');
-        $results = $manager->getInserted();
-        $this->assertEquals(['articles'], $results);
-    }
-}

+ 149 - 0
tests/TestCase/TestSuite/Fixture/FixtureHelperTest.php

@@ -0,0 +1,149 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         5.0.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\TestCase\TestSuite;
+
+use Cake\Datasource\ConnectionManager;
+use Cake\Test\Fixture\ArticlesFixture;
+use Cake\TestSuite\Fixture\FixtureHelper;
+use Cake\TestSuite\Fixture\TestFixture;
+use Cake\TestSuite\TestCase;
+use Company\TestPluginThree\Test\Fixture\ArticlesFixture as CompanyArticlesFixture;
+use TestPlugin\Test\Fixture\ArticlesFixture as PluginArticlesFixture;
+use TestPlugin\Test\Fixture\Blog\CommentsFixture as PluginCommentsFixture;
+use UnexpectedValueException;
+
+class FixtureHelperTest extends TestCase
+{
+    protected $fixtures = ['core.Articles'];
+
+    /**
+     * Clean up after test.
+     */
+    protected function tearDown(): void
+    {
+        parent::tearDown();
+        $this->clearPlugins();
+        ConnectionManager::dropAlias('test1');
+        ConnectionManager::dropAlias('test2');
+    }
+
+    /**
+     * Tests loading fixtures.
+     */
+    public function testLoadFixtures(): void
+    {
+        $this->setAppNamespace('TestApp');
+        $this->loadPlugins(['TestPlugin']);
+        $fixtures = (new FixtureHelper())->loadFixtures([
+            'core.Articles',
+            'plugin.TestPlugin.Articles',
+            'plugin.TestPlugin.Blog/Comments',
+            'plugin.Company/TestPluginThree.Articles',
+        ]);
+        $this->assertNotEmpty($fixtures);
+        $this->assertInstanceOf(ArticlesFixture::class, $fixtures[ArticlesFixture::class]);
+        $this->assertInstanceOf(PluginArticlesFixture::class, $fixtures[PluginArticlesFixture::class]);
+        $this->assertInstanceOf(PluginCommentsFixture::class, $fixtures[PluginCommentsFixture::class]);
+        $this->assertInstanceOf(CompanyArticlesFixture::class, $fixtures[CompanyArticlesFixture::class]);
+    }
+
+    /**
+     * Tests loading missing fixtures.
+     */
+    public function testLoadMissingFixtures(): void
+    {
+        $this->expectException(UnexpectedValueException::class);
+        $this->expectExceptionMessage('Could not find fixture `core.ThisIsMissing`');
+        (new FixtureHelper())->loadFixtures(['core.ThisIsMissing']);
+    }
+
+    /**
+     * Tests loading duplicate fixtures.
+     */
+    public function testLoadDulicateFixtures(): void
+    {
+        $this->expectException(UnexpectedValueException::class);
+        $this->expectExceptionMessage('Found duplicate fixture `core.Articles`');
+        (new FixtureHelper())->loadFixtures(['core.Articles','core.Articles']);
+    }
+
+    /**
+     * Tests running callback per connection
+     */
+    public function testPerConnection(): void
+    {
+        $fixture1 = $this->createMock(TestFixture::class);
+        $fixture1->expects($this->once())
+            ->method('connection')
+            ->will($this->returnValue('test1'));
+
+        $fixture2 = $this->createMock(TestFixture::class);
+        $fixture2->expects($this->once())
+            ->method('connection')
+            ->will($this->returnValue('test2'));
+
+        ConnectionManager::alias('test', 'test1');
+        ConnectionManager::alias('test', 'test2');
+
+        $numCalls = 0;
+        (new FixtureHelper())->runPerConnection(function () use (&$numCalls) {
+            ++$numCalls;
+        }, [$fixture1, $fixture2]);
+        $this->assertSame(2, $numCalls);
+    }
+
+    /**
+     * Tests inserting fixtures.
+     */
+    public function testInsertFixtures(): void
+    {
+        /**
+         * @var \Cake\Database\Connection $connection
+         */
+        $connection = ConnectionManager::get('test');
+        $connection->newQuery()->delete('articles')->execute()->closeCursor();
+        $rows = $connection->newQuery()->select('*')->from('articles')->execute();
+        $this->assertEmpty($rows->fetchAll());
+        $rows->closeCursor();
+
+        $helper = new FixtureHelper();
+        $helper->insert($helper->loadFixtures(['core.Articles']));
+        $rows = $connection->newQuery()->select('*')->from('articles')->execute();
+        $this->assertNotEmpty($rows->fetchAll());
+        $rows->closeCursor();
+    }
+
+    /**
+     * Tests truncating fixtures.
+     */
+    public function testTruncateFixtures(): void
+    {
+        /**
+         * @var \Cake\Database\Connection $connection
+         */
+        $connection = ConnectionManager::get('test');
+        $rows = $connection->newQuery()->select('*')->from('articles')->execute();
+        $this->assertNotEmpty($rows->fetchAll());
+        $rows->closeCursor();
+
+        $helper = new FixtureHelper();
+        $helper->truncate($helper->loadFixtures(['core.Articles']));
+        $rows = $connection->newQuery()->select('*')->from('articles')->execute();
+        $this->assertEmpty($rows->fetchAll());
+        $rows->closeCursor();
+    }
+}

+ 52 - 0
tests/TestCase/TestSuite/Fixture/TransactionFixtureStrategyTest.php

@@ -0,0 +1,52 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         5.0.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\TestCase\TestSuite;
+
+use Cake\Datasource\ConnectionManager;
+use Cake\TestSuite\Fixture\TransactionFixtureStrategy;
+use Cake\TestSuite\TestCase;
+
+class TransactionFixtureStrategyTest extends TestCase
+{
+    protected $fixtures = ['core.Articles'];
+
+    /**
+     * Tests truncation strategy.
+     */
+    public function testStrategy(): void
+    {
+        /**
+         * @var \Cake\Database\Connection $connection
+         */
+        $connection = ConnectionManager::get('test');
+        $connection->newQuery()->delete('articles')->execute()->closeCursor();
+        $rows = $connection->newQuery()->select('*')->from('articles')->execute();
+        $this->assertEmpty($rows->fetchAll());
+        $rows->closeCursor();
+
+        $strategy = new TransactionFixtureStrategy();
+        $strategy->setupTest(['core.Articles']);
+        $rows = $connection->newQuery()->select('*')->from('articles')->execute();
+        $this->assertNotEmpty($rows->fetchAll());
+        $rows->closeCursor();
+
+        $strategy->teardownTest();
+        $rows = $connection->newQuery()->select('*')->from('articles')->execute();
+        $this->assertEmpty($rows->fetchAll());
+        $rows->closeCursor();
+    }
+}

+ 0 - 45
tests/TestCase/TestSuite/Fixture/TransactionResetStrategyTest.php

@@ -1,45 +0,0 @@
-<?php
-declare(strict_types=1);
-
-/**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link          https://cakephp.org CakePHP(tm) Project
- * @since         4.3.0
- * @license       https://opensource.org/licenses/mit-license.php MIT License
- */
-namespace Cake\Test\TestCase\TestSuite;
-
-use Cake\ORM\TableRegistry;
-use Cake\TestSuite\Fixture\TransactionResetStrategy;
-use Cake\TestSuite\TestCase;
-
-class TransactionResetStrategyTest extends TestCase
-{
-    public $fixtures = ['core.Users'];
-
-    /**
-     * Test that beforeTest starts a transaction that afterTest closes.
-     */
-    public function testTransactionWrapping(): void
-    {
-        $users = TableRegistry::getTableLocator()->get('Users');
-
-        $strategy = new TransactionResetStrategy($this->fixtureManager);
-        $strategy->setupTest();
-        $user = $users->newEntity(['username' => 'testing', 'password' => 'secrets']);
-
-        $users->save($user, ['atomic' => true]);
-        $this->assertNotEmpty($users->get($user->id), 'User should exist.');
-
-        // Rollback and the user should be gone.
-        $strategy->teardownTest();
-        $this->assertNull($users->findById($user->id)->first(), 'No user expected.');
-    }
-}

+ 52 - 0
tests/TestCase/TestSuite/Fixture/TruncateFixtureStrategyTest.php

@@ -0,0 +1,52 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link          https://cakephp.org CakePHP(tm) Project
+ * @since         5.0.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\TestCase\TestSuite;
+
+use Cake\Datasource\ConnectionManager;
+use Cake\TestSuite\Fixture\TruncateFixtureStrategy;
+use Cake\TestSuite\TestCase;
+
+class TruncateFixtureStrategyTest extends TestCase
+{
+    protected $fixtures = ['core.Articles'];
+
+    /**
+     * Tests truncation strategy.
+     */
+    public function testStrategy(): void
+    {
+        /**
+         * @var \Cake\Database\Connection $connection
+         */
+        $connection = ConnectionManager::get('test');
+        $connection->newQuery()->delete('articles')->execute()->closeCursor();
+        $rows = $connection->newQuery()->select('*')->from('articles')->execute();
+        $this->assertEmpty($rows->fetchAll());
+        $rows->closeCursor();
+
+        $strategy = new TruncateFixtureStrategy();
+        $strategy->setupTest(['core.Articles']);
+        $rows = $connection->newQuery()->select('*')->from('articles')->execute();
+        $this->assertNotEmpty($rows->fetchAll());
+        $rows->closeCursor();
+
+        $strategy->teardownTest();
+        $rows = $connection->newQuery()->select('*')->from('articles')->execute();
+        $this->assertEmpty($rows->fetchAll());
+        $rows->closeCursor();
+    }
+}

+ 0 - 51
tests/TestCase/TestSuite/Fixture/TruncationResetStrategyTest.php

@@ -1,51 +0,0 @@
-<?php
-declare(strict_types=1);
-
-/**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link          https://cakephp.org CakePHP(tm) Project
- * @since         4.3.0
- * @license       https://opensource.org/licenses/mit-license.php MIT License
- */
-namespace Cake\Test\TestCase\TestSuite;
-
-use Cake\ORM\TableRegistry;
-use Cake\TestSuite\Fixture\TruncationResetStrategy;
-use Cake\TestSuite\TestCase;
-
-/**
- * TruncationStrategy test
- */
-class TruncationResetStrategyTest extends TestCase
-{
-    public $fixtures = ['core.Articles', 'core.Tags', 'core.ArticlesTags'];
-
-    /**
-     * Test that beforeTest truncates tables from the previous test
-     */
-    public function testTeardownSimple(): void
-    {
-        $articles = TableRegistry::getTableLocator()->get('Articles');
-        $articlesTags = TableRegistry::getTableLocator()->get('ArticlesTags');
-
-        $rowCount = $articles->find()->count();
-        $this->assertGreaterThan(0, $rowCount);
-        $rowCount = $articlesTags->find()->count();
-        $this->assertGreaterThan(0, $rowCount);
-
-        $strategy = new TruncationResetStrategy($this->fixtureManager);
-        $strategy->teardownTest();
-
-        $rowCount = $articles->find()->count();
-        $this->assertEquals(0, $rowCount);
-        $rowCount = $articlesTags->find()->count();
-        $this->assertEquals(0, $rowCount);
-    }
-}

+ 0 - 35
tests/TestCase/TestSuite/TestCaseTest.php

@@ -26,11 +26,8 @@ use Cake\ORM\Table;
 use Cake\Routing\Exception\MissingRouteException;
 use Cake\Routing\Router;
 use Cake\Test\Fixture\FixturizedTestCase;
-use Cake\TestSuite\Fixture\ResetStrategyInterface;
-use Cake\TestSuite\Fixture\TruncationResetStrategy;
 use Cake\TestSuite\TestCase;
 use PHPUnit\Framework\AssertionFailedError;
-use RuntimeException;
 use TestApp\Model\Table\SecondaryPostsTable;
 
 /**
@@ -411,36 +408,4 @@ class TestCaseTest extends TestCase
         $result = Router::url($url);
         $this->assertSame('/app/articles', $result);
     }
-
-    /**
-     * Test getting the state reset manager.
-     */
-    public function testGetResetStrategySuccess(): void
-    {
-        $instance = $this->getResetStrategy();
-        $this->assertInstanceOf(ResetStrategyInterface::class, $instance);
-        $this->assertInstanceOf(TruncationResetStrategy::class, $instance);
-    }
-
-    /**
-     * Test getting the state reset manager when class is invalid
-     */
-    public function testGetResetStrategyMissingClass(): void
-    {
-        $this->expectException(RuntimeException::class);
-        $this->expectExceptionMessage('Cannot find class `NotThere`');
-        $this->stateResetStrategy = 'NotThere';
-        $this->getResetStrategy();
-    }
-
-    /**
-     * Test getting the state reset manager when class is invalid
-     */
-    public function testGetStateResetInvalidClass(): void
-    {
-        $this->expectException(RuntimeException::class);
-        $this->expectExceptionMessage('does not implement the required');
-        $this->stateResetStrategy = static::class;
-        $this->getResetStrategy();
-    }
 }