'Laravel Test intermittently failing due to 1 second difference

I have the following type of test for most models across my application:

     * @test
     */
    public function an_authorised_user_can_delete_a_contact()
    {
        $contact = Contact::factory()->create();
        $this->assertDatabaseHas('contacts', $contact->getAttributes());

        $response = $this->actingAs($this->user_delete)
            ->delete('/contacts/'.$contact->id);

        $this->assertSoftDeleted('contacts', $contact->getAttributes());

        $response->assertRedirect('contacts');
    }

This works well most of the time, but every now and then will fail due to the timestamps being slightly out.

Failed asserting that any soft deleted row in the table [contacts] matches the attributes {"first_name":"Katherine","last_name":"Will","title":"Jewelry Model OR Mold Makers","telephone":"+15127653255","mobile":"+19366193055","email":"[email protected]","vendor_id":1,"updated_at":"2022-04-04 18:09:50","created_at":"2022-04-04 18:09:50","id":1}.

Found: [ { "id": 1, "first_name": "Katherine", "last_name": "Will", "title": "Jewelry Model OR Mold Makers", "telephone": "+15127653255", "mobile": "+19366193055", "email": "[email protected]", "vendor_id": 1, "created_at": "2022-04-04 18:09:50", "updated_at": "2022-04-04 18:09:51", "deleted_at": "2022-04-04 18:09:51" } ]

The difference is in updated_at - "2022-04-04 18:09:50" vs "2022-04-04 18:09:51".

Is there a better way to structure the test to make it more robust?



Solution 1:[1]

1st solution:

Use Carbon::setTestNow('2022-04-04 18:09:50') , this will make timestamps not change.

2nd solution (recommended):

Since you wrote the test to check if an authorized user can delete a contact I would not check all attributes as you did but instead would assert the model itself, which is recommended on laravel documentation, here's the link:

$this->assertSoftDeleted($contact);

or only check with the id

$this->assertSoftDeleted('contacts', [
    'id' => $contact->id
]);

You can also check out this answer on laracast forum maybe it well help you more.

Solution 2:[2]

After deleting the contact record, you should fetch a fresh instance of the model. Try this:

...

$deletedContact = $contact->fresh();
$this->assertSoftDeleted('contacts', $deletedContact->getAttributes());
$response->assertRedirect('contacts');

I would also remove $this->assertDatabaseHas('contacts', $contract->getAttributes()); to make the test run a bit faster. When writing tests you should be testing your own code, not the Laravel framework.

Solution 3:[3]

In your factory try to override the "created_at" attribute an hour ago.

public function definition()
{
    return [
        'created_at'=> $this->faker->dateTimeBetween('-1 hour' );,
        'updated_at'=> \Carbon\Carbon::now()->timestamp;
    ];
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Njazi Shehu
Solution 2
Solution 3 Nehal