Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

bretterer's avatar

Testing for database transaction rollback

I am struggling testing to confirm that my DB::rollback is working fine. I have a model, Event::create() being called from a livewire component. My test uses the app instance to swap Event::class with mock where I call shouldReceive('create') and throw an exception:

#[Test]
    public function rolls_back_transaction_on_failure(): void
    {
        $user = User::factory()->create();

        $this->instance(
            Event::class,
            Mockery::mock(Event::class, function (MockInterface $mock) {
                $mock->expects('create')->andThrow(new \Exception('Test exception'));
            })
        );

        Livewire::actingAs($user)
            ->test(Start::class)
            ->set('state.organization_name', 'Test Organization')
            ->set('state.event_name', 'Test Event')
            ->set('state.event_start_date', '2025-01-01 09:00:00')
            ->call('createOrganization')
            ->assertHasErrors(['createOrganization' => 'Test exception']);

        // Assert nothing was created due to rollback
        $this->assertDatabaseCount('organizations', 0);
        $this->assertDatabaseCount('events', 0);
        $this->assertNull($user->refresh()->current_organization);
        $this->assertNull($user->refresh()->current_event);
    }

My Livewire code that is called is as follows:

When I run the test, it is logging the message right before DB::commit(), not the log message in the exception. Can anyone please guide me here?

0 likes
2 replies
LaryAI's avatar
Level 58

The issue you're encountering is likely due to the fact that the Event::create() method is being mocked, and the exception is not being thrown as expected. This means that the transaction is not being rolled back because the exception is not actually occurring within the transaction block.

To ensure that the exception is thrown and the transaction is rolled back, you need to make sure that the mock is correctly set up to throw the exception when Event::create() is called. Here's a step-by-step solution to address this:

  1. Ensure the Mock is Correctly Set Up: Double-check that the mock is properly configured to throw an exception when Event::create() is called. You might want to use shouldReceive instead of expects to ensure the method is mocked correctly.

  2. Verify the Exception is Thrown: Make sure that the exception is actually being thrown during the test execution. You can add a temporary dd() or Log::info() statement in the catch block to verify that the code is reaching there.

  3. Check the Test Setup: Ensure that the test is correctly set up to swap the Event class with the mock. The instance method should be correctly replacing the Event class in the container.

Here's an updated version of your test with some adjustments:

#[Test]
public function rolls_back_transaction_on_failure(): void
{
    $user = User::factory()->create();

    $this->instance(
        Event::class,
        Mockery::mock(Event::class, function (MockInterface $mock) {
            $mock->shouldReceive('create')->andThrow(new \Exception('Test exception'));
        })
    );

    Livewire::actingAs($user)
        ->test(Start::class)
        ->set('state.organization_name', 'Test Organization')
        ->set('state.event_name', 'Test Event')
        ->set('state.event_start_date', '2025-01-01 09:00:00')
        ->call('createOrganization')
        ->assertHasErrors(['createOrganization' => 'Test exception']);

    // Assert nothing was created due to rollback
    $this->assertDatabaseCount('organizations', 0);
    $this->assertDatabaseCount('events', 0);
    $this->assertNull($user->refresh()->current_organization);
    $this->assertNull($user->refresh()->current_event);
}
  1. Debugging: If the issue persists, add logging or debugging statements in your Livewire component to ensure that the exception is being caught and the rollback is being triggered.

  2. Check the Livewire Component: Ensure that the createOrganization method in your Livewire component is correctly handling the exception and performing the rollback.

By following these steps, you should be able to confirm that the transaction is rolled back when an exception is thrown during the Event::create() call. If the problem persists, consider checking the Laravel documentation or forums for any known issues with mocking and transactions in your specific Laravel version.

Please or to participate in this conversation.