So I'm doing some unit testing before implementing new features. I run the test but it fails with OverflowException: Maximum retries reached 10000 but unique value not found
This is the test I am running.
public function test_job_factory() { $client = Client::factory()->create(); $accountHandler = AccountHandler::factory()->create(); $user = User::factory()->create(); $this->post('/login', [ 'email' => $user->email, 'password' => 'password', ]); $user->givePermissionTo( 'manage jobs' ); $clientContact = ClientContact::factory()->create(); $job = Job::factory()->create(); $this->assertTrue($job->id > 0); }
The error seems to occur when creating the job itself. The test above tested other plants and is working.
This is the JobFactory.php file:
<?php namespace Database\Factories; use App\Models\AccountHandler; use App\Models\Client; use App\Models\ClientContact; use App\Models\Job; use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Job> */ class JobFactory extends Factory { protected $model = Job::class; /** * Define the model's default state. * * @return array<string, mixed> */ public function definition() { return [ 'title' => $this->faker->company(), 'is_active' => 1, 'is_draft' => 0, 'client_id' => $this->faker->unique()->numberBetween(1, Client::count()), 'account_handler_id' => $this->faker->unique()->numberBetween(1, AccountHandler::count()), 'eclipse_contact_id' => $this->faker->unique()->numberBetween(1, User::count()), 'client_contact_id' => $this->faker->unique()->numberBetween(1, ClientContact::count()), 'description' => $this->faker->paragraphs(1), ]; } }
And the migration for this (create_jobs_table.php):
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('jobs', function (Blueprint $table) { $table->id('number'); $table->boolean( 'is_active' )->default(true); $table->boolean( 'is_complete' )->default(true); $table->string('title', 64)->nullable(); $table->timestamps(); $table->foreignId('author')->nullable()->constrained()->references('id')->on('users'); $table->text('description')->nullable(); $table->foreignId('client_id')->nullable()->constrained()->references('id')->on('clients'); $table->foreignId('client_contact_id')->nullable()->constrained()->references('id')->on('client_contacts'); $table->foreignId('account_handler_id')->nullable()->constrained()->references('id')->on('account_handlers'); $table->date('expiry_date')->nullable(); $table->date('artwork_deadline')->nullable(); $table->date('proof_deadline')->nullable(); $table->integer('redirect')->nullable(); $table->boolean( 'is_draft' )->default(true); $table->foreignId('eclipse_contact_id')->nullable()->constrained()->references('id')->on('users'); $table->foreignId('subscription_id')->nullable()->constrained()->references('id')->on('subscriptions'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('jobs'); } };
So what went wrong and why does this loop occur? I've added the minimum data required to create the job, so not sure what I'm missing.
Thanks
*** edit *** I was asked to provide a link to where I found the bad practice of using unique and numberBetween, here is an example This won't work like you're looking for!
The problem is when you try to get 5 values between 1 and 5 (Inclusive), you can only take 4. The 5th number will never exceed
unique()
Verification.This is because, for example, when you run the first instance of unique
Then run it in another factory, laravel usually extends the previous instance (I guess), if that makes sense. But when you pass true like below
It starts searching again from 1 to 20
This solved my problem.
Therefore always pass true
instancesin
unique()I recommend following the documented approach to solving this problem. https://laravel.com/docs/9.x/database-test#factoryrelationship
Looking at your migration table, there's a good chance your factory won't work because you're indicating the relationship at the SQL level, which basically forces you to have the records in the related table, otherwise SQL will throw an error..