Cvetan's avatar
Level 6

Asserting storage delete fails when testing

I have event and listener, when user uploads new profile image listener triggers and if paths is different than default avatar it deletes that image. That functionility works, but I can't make it work in tests.

Listener is like this:

<?php

namespace App\Listeners;

use App\Events\ProfileImageUpdated;
use Illuminate\Support\Facades\Storage;

class DeleteOldProfileImage
{
    /**
     * Handle the event.
     */
    public function handle(ProfileImageUpdated $event): void
    {
        $image = $event->oldProfileImage;

        if ($image === config('users.default_avatar')) {
            return;
        }

        Storage::delete('public/' . $image);
    }
}

And here is the test:

public function test_old_avatar_will_be_deleted(): void
    {
        $user = User::factory()->create();
        $userData = [
            'first_name' => 'John',
            'last_name' => 'Doe',
            'email' => '[email protected]',
            'phone_number' => '0656881180',
            'avatar' => UploadedFile::fake()
                ->image('avatar.png', 100, 100)
        ];

        Storage::fake('public');
        Event::fake();

        $this->actingAs($user)
            ->patch('/profile', $userData);
        $user->refresh();

        $oldAvatar = $user->avatar;

        $this->assertTrue(Storage::disk('public')->exists($oldAvatar));

        $userData['avatar'] = UploadedFile::fake()
            ->image('profile.png', 100, 100);
        $this->actingAs($user)
            ->patch('/profile', $userData);
        $user->refresh();

        $this->assertNotSame($oldAvatar, $user->avatar);
        $this->assertFalse(Storage::disk('public')->exists($oldAvatar));
        $this->assertTrue(Storage::disk('public')->exists($user->avatar));
    }

I call patching profile twice, because after first call default image won't be deleted and after second it should be. I also dumped paths both in listener and in test, in test path is appropriate uploads/users/avatars...png and path in listener is /tmp/...

Am I doing something wrong here?

0 likes
1 reply
LaryAI's avatar
Level 58

It looks like the issue you're encountering is related to the paths being different in your test environment compared to your actual application. When using Storage::fake('public'), the paths might not align as expected. Here’s a step-by-step solution to ensure your test works correctly:

  1. Ensure the Event is Dispatched: Since you are faking events, you need to ensure that the ProfileImageUpdated event is dispatched correctly.

  2. Check the Path Consistency: Ensure that the paths used in your listener and test are consistent.

  3. Use the Correct Disk: Make sure you are using the same disk in both your listener and your test.

Here’s an updated version of your test:

public function test_old_avatar_will_be_deleted(): void
{
    $user = User::factory()->create();
    $userData = [
        'first_name' => 'John',
        'last_name' => 'Doe',
        'email' => '[email protected]',
        'phone_number' => '0656881180',
        'avatar' => UploadedFile::fake()->image('avatar.png', 100, 100)
    ];

    Storage::fake('public');
    Event::fake();

    // First profile update
    $this->actingAs($user)->patch('/profile', $userData);
    $user->refresh();

    $oldAvatar = $user->avatar;

    $this->assertTrue(Storage::disk('public')->exists($oldAvatar));

    // Second profile update with a new avatar
    $userData['avatar'] = UploadedFile::fake()->image('profile.png', 100, 100);
    $this->actingAs($user)->patch('/profile', $userData);
    $user->refresh();

    // Ensure the old avatar is deleted and the new one exists
    $this->assertNotSame($oldAvatar, $user->avatar);
    $this->assertFalse(Storage::disk('public')->exists($oldAvatar));
    $this->assertTrue(Storage::disk('public')->exists($user->avatar));

    // Ensure the event was dispatched
    Event::assertDispatched(ProfileImageUpdated::class, function ($event) use ($oldAvatar) {
        return $event->oldProfileImage === $oldAvatar;
    });
}

Explanation:

  1. Storage::fake('public'): This ensures that the storage disk is faked for the test environment.
  2. Event::fake(): This fakes the event dispatching.
  3. Event::assertDispatched: This ensures that the ProfileImageUpdated event was dispatched with the correct old avatar path.

Listener Update:

Ensure your listener is correctly handling the storage disk:

namespace App\Listeners;

use App\Events\ProfileImageUpdated;
use Illuminate\Support\Facades\Storage;

class DeleteOldProfileImage
{
    /**
     * Handle the event.
     */
    public function handle(ProfileImageUpdated $event): void
    {
        $image = $event->oldProfileImage;

        if ($image === config('users.default_avatar')) {
            return;
        }

        Storage::disk('public')->delete($image);
    }
}

Conclusion:

By ensuring the paths are consistent and the event is correctly dispatched and handled, your test should now correctly assert that the old avatar is deleted when a new one is uploaded.

Please or to participate in this conversation.