Testing insert methods by using factory make of Laravel with a PostgreSQL reserved id

744 Views Asked by At

Our Laravel app uses PostgreSQL and before we insert a register in a table we reserve the next valid id by calling PostgreSQL nextval function.

I want to test the insert method of UserRepository. As our database expects to receive an id I would need to generate it also in the test method that tests the insert method, so I use Laravel's factories make method like this to create a fake id:

$userModel = UserModel::factory()->make([
    'id' => $this->userRepository->nextValId()
    // Another solution 'id' => $this->faker->unique()->randomNumber(5)
]);
$this->userRepository->insert($userModel->toArray());

This might not seem too complex, but imagine I now have an insertMany method that inserts several users at once, and I want to test this insertMany method. In this case the creation with Laravel's factory() gets more complicated. To start off, this code wouldn't work now as the ids of all users would be the same:

$collection = UserModel::factory(4)->make([
    'id' => $this->userRepository->nextValId()
    // Another solution 'id' => $this->faker->unique()->randomNumber(5)
]);
$this->userRepository->insertMany($collection->toArray());

So I have to rewrite it like this:

$collection = UserModel::factory(4)->make();
$users = $collection->toArray();
$users[0]['id'] = $this->userRepository->nextValId();
$users[1]['id'] = $this->userRepository->nextValId();
$users[2]['id'] = $this->userRepository->nextValId();
$users[3]['id'] = $this->userRepository->nextValId();
$this->userRepository->insertMany($users);

So the question is: is there a one-liner for this use case? Is there a way to tell inside the array of the make that a key has to be different for every model created by the factory? (In this case, I would want $this->userRepository->nextValId() to act for each one of the models created.)

Of course I do not consider using Laravel's factories create method as it would create the register already in the database table and the later insertion would throw a duplicate key exception.

Side note: this is a fake example, not real, so it is possible that there might be a little bug. The code pasted is not proved. Mine is different, but the idea is the same as the one in the example used.

1

There are 1 best solutions below

0
On

I have found a solution using Laravel's factory sequences: https://laravel.com/docs/9.x/database-testing#sequences

This would be the cleanest code in my opinion:

$collection = UserModel::factory()->count(4)->state(new Sequence(
    fn ($sequence) => ['id' => $this->userRepository->nextValId()]
    // Or this using DatabaseTransactions trait: fn ($sequence) => ['id' => $sequence->index + 1]
))->make();
$this->userRepository->insertMany($collection->toArray());