diff --git a/system/Test/CIDatabaseTestCase.php b/system/Test/CIDatabaseTestCase.php index b1354ec73c88..bf4b03561aae 100644 --- a/system/Test/CIDatabaseTestCase.php +++ b/system/Test/CIDatabaseTestCase.php @@ -194,6 +194,9 @@ protected function setUp(): void $this->migrations->latest('tests'); } } + + // Reset counts on faked items + Fabricator::resetCounts(); } if (! empty($this->seed)) diff --git a/system/Test/Fabricator.php b/system/Test/Fabricator.php index d9c1a689bee9..aef3f5639278 100644 --- a/system/Test/Fabricator.php +++ b/system/Test/Fabricator.php @@ -50,6 +50,13 @@ */ class Fabricator { + /** + * Array of counts for fabricated items + * + * @var array + */ + protected static $tableCounts = []; + /** * Locale-specific Faker instance * @@ -152,20 +159,64 @@ public function __construct($model, array $formatters = null, string $locale = n $this->setFormatters($formatters); } + //-------------------------------------------------------------------- + + /** + * Reset internal counts + */ + public static function resetCounts() + { + self::$tableCounts = []; + } + /** - * Reset state to defaults + * Get the count for a specific table * - * @return $this + * @param string $table Name of the target table + * + * @return integer + */ + public static function getCount(string $table): int + { + return empty(self::$tableCounts[$table]) ? 0 : self::$tableCounts[$table]; + } + + /** + * Set the count for a specific table + * + * @param string $table Name of the target table + * @param integer $count Count value + * + * @return integer The new count value */ - public function reset(): self + public static function setCount(string $table, int $count): int { - $this->setFormatters(); + self::$tableCounts[$table] = $count; + return $count; + } - $this->overrides = $this->tempOverrides = []; - $this->locale = config('App')->defaultLocale; - $this->faker = Factory::create($this->locale); + /** + * Increment the count for a table + * + * @param string $table Name of the target table + * + * @return integer The new count value + */ + public static function upCount(string $table): int + { + return self::setCount($table, self::getCount($table) + 1); + } - return $this; + /** + * Decrement the count for a table + * + * @param string $table Name of the target table + * + * @return integer The new count value + */ + public static function downCount(string $table): int + { + return self::setCount($table, self::getCount($table) - 1); } //-------------------------------------------------------------------- @@ -515,7 +566,11 @@ public function create(int $count = null, bool $mock = false) // Iterate over new entities and insert each one, storing insert IDs foreach ($this->make($count ?? 1) as $result) { - $ids[] = $this->model->insert($result, true); + if ($id = $this->model->insert($result, true)) + { + $ids[] = $id; + self::upCount($this->model->table); + } } // If the model defines a "withDeleted" method for handling soft deletes then use it diff --git a/tests/system/Database/Live/FabricatorLiveTest.php b/tests/system/Database/Live/FabricatorLiveTest.php index 96a9b8f99b8a..7356b68deb9c 100644 --- a/tests/system/Database/Live/FabricatorLiveTest.php +++ b/tests/system/Database/Live/FabricatorLiveTest.php @@ -45,4 +45,25 @@ public function testHelperCreates() $this->seeInDatabase('user', ['name' => $result->name]); } + + public function testCreateIncrementsCount() + { + $fabricator = new Fabricator(UserModel::class); + $fabricator->setOverrides(['country' => 'China']); + + $count = Fabricator::getCount('user'); + + $fabricator->create(); + + $this->assertEquals($count + 1, Fabricator::getCount('user')); + } + + public function testHelperIncrementsCount() + { + $count = Fabricator::getCount('user'); + + fake(UserModel::class, ['country' => 'Italy']); + + $this->assertEquals($count + 1, Fabricator::getCount('user')); + } } diff --git a/tests/system/Test/FabricatorTest.php b/tests/system/Test/FabricatorTest.php index 9ad3517414be..4283bef67524 100644 --- a/tests/system/Test/FabricatorTest.php +++ b/tests/system/Test/FabricatorTest.php @@ -21,6 +21,13 @@ class FabricatorTest extends CIUnitTestCase 'deleted_at' => 'date', ]; + protected function tearDown(): void + { + parent::tearDown(); + + Fabricator::resetCounts(); + } + //-------------------------------------------------------------------- public function testConstructorWithString() @@ -398,4 +405,82 @@ public function testCreateMockSetsDatabaseFields() $this->assertObjectHasAttribute('deleted_at', $result); $this->assertNull($result->deleted_at); } + + //-------------------------------------------------------------------- + + public function testSetCountReturnsCount() + { + $result = Fabricator::setCount('goblins', 42); + + $this->assertEquals(42, $result); + } + + public function testSetCountSetsValue() + { + Fabricator::setCount('trolls', 3); + $result = Fabricator::getCount('trolls'); + + $this->assertEquals(3, $result); + } + + public function testGetCountNewTableReturnsZero() + { + $result = Fabricator::getCount('gremlins'); + + $this->assertEquals(0, $result); + } + + public function testUpCountIncrementsValue() + { + Fabricator::setCount('orcs', 12); + Fabricator::upCount('orcs'); + + $this->assertEquals(13, Fabricator::getCount('orcs')); + } + + public function testUpCountReturnsValue() + { + Fabricator::setCount('hobgoblins', 12); + $result = Fabricator::upCount('hobgoblins'); + + $this->assertEquals(13, $result); + } + + public function testUpCountNewTableReturnsOne() + { + $result = Fabricator::upCount('ogres'); + + $this->assertEquals(1, $result); + } + + public function testDownCountDecrementsValue() + { + Fabricator::setCount('orcs', 12); + Fabricator::downCount('orcs'); + + $this->assertEquals(11, Fabricator::getCount('orcs')); + } + + public function testDownCountReturnsValue() + { + Fabricator::setCount('hobgoblins', 12); + $result = Fabricator::downCount('hobgoblins'); + + $this->assertEquals(11, $result); + } + + public function testDownCountNewTableReturnsNegativeOne() + { + $result = Fabricator::downCount('ogres'); + + $this->assertEquals(-1, $result); + } + + public function testResetClearsValue() + { + Fabricator::setCount('giants', 1000); + Fabricator::resetCounts(); + + $this->assertEquals(0, Fabricator::getCount('giants')); + } } diff --git a/user_guide_src/source/testing/fabricator.rst b/user_guide_src/source/testing/fabricator.rst index 8b630894f81d..8a29680e675c 100644 --- a/user_guide_src/source/testing/fabricator.rst +++ b/user_guide_src/source/testing/fabricator.rst @@ -223,3 +223,58 @@ This is equivalent to:: $fabricator = new Fabricator('App\Models\UserModel'); $fabricator->setOverrides(['name' => 'Gerry']); $user = $fabricator->create(); + +Table Counts +============ + +Frequently your faked data will depend on other faked data. ``Fabricator`` provides a static +count of the number of faked items you have created for each table. Consider the following +example: + +Your project has users and groups. In your test case you want to create various scenarios +with groups of different sizes, so you use ``Fabricator`` to create a bunch of groups. +Now you want to create fake users but don't want to assign them to a non-existant group ID. +Your model's fake method could look like this:: + + class UserModel + { + protected $table = 'users'; + + public function fake(Generator &$faker) + { + return [ + 'first' => $faker->firstName, + 'email' => $faker->email, + 'group_id' => rand(1, Fabricator::getCount('users')), + ]; + } + +Now creating a new user will ensure it is a part of a valid group: ``$user = fake(UserModel::class);`` + +``Fabricator`` handles the counts internally but you can also access these static methods +to assist with using them: + +**getCount(string $table): int** + +Return the current value for a specific table (default: 0). + +**setCount(string $table, int $count): int** + +Set the value for a specific table manually, for example if you create some test items +without using a fabricator that you still wanted factored into the final counts. + +**upCount(string $table): int** + +Increment the value for a specific table by one and return the new value. (This is what is +used internally with ``Fabricator::create()``). + +**downCount(string $table): int** + +Decrement the value for a specific table by one and return the new value, for example if +you deleted a fake item but wanted to track the change. + +**resetCounts()** + +Resets all counts. Good idea to call this between test cases (though using +``CIDatabaseTestCase::$refresh = true`` does it automatically). +