My storage fake doesn’t store the image inside the test directory

I’m writing a test for my Item model, it has images to be uploaded and I expect it should be stored inside the testing directory to be cleared each time I ran the test. But it wasn’t. Instead, it creates a new directory named item-images in both storage and testing/disks but it uses the actual storage/item-images directory instead of storage/disks/item-images.

Storage::fake('item-images') is already defined on top of the test case, I’m a bit confused by this, please help.

enter image description here

tests/Feature/ItemTest.php

public function test_user_can_store_new_item()
{
    Storage::fake('item-images');
    Sanctum::actingAs(User::factory()->create());

    $images = [
        UploadedFile::fake()->image('image1.png'),
        UploadedFile::fake()->image('image2.png'),
        UploadedFile::fake()->image('image3.png'),
    ];

    $item = Item::factory()->make(['images' => $images]);

    $this->postJson(route('items.store'), $item->toArray())
        ->dump()
        ->assertCreated()
        ->assertJsonFragment($item->toArray());

    foreach ($images as $image) {
        Storage::disk('item-images')->assertExists($image->hashName());
        Storage::disk('item-images')->assertMissing('missing.png');
    }
}

app/Models/Item.php

<?php

namespace AppModels;

use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentRelationsBelongsTo;

class Item extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var string[]
     */
    protected $fillable = [
        'category_id',
        'name',
        'description',
        'history',
    ];

    protected $with = ['category'];

    /**
     * Get the category that owns the Item
     *
     * @return IlluminateDatabaseEloquentRelationsBelongsTo
     */
    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }
}

app/Http/Controllers/ItemController.php

/**
 * Store a newly created resource in storage.
 *
 * @param  AppHttpRequestsRequestItem  $request
 * @return IlluminateHttpResponse
 */
public function store(RequestItem $request)
{
    $data = $request->validated();

    $item = Item::create(Arr::except($data, 'images'));

    if ($request->has('images')) {
        foreach ($request->file('images') as $image) {
            $path = $image->store('item-images');
            Image::create(['item_id' => $item->id, 'url' => $path]);
        }
    }

    return response()->json($item, 201);
}

app/Http/Requests/RequestItem.php

<?php

namespace AppHttpRequests;

use IlluminateFoundationHttpFormRequest;
use IlluminateValidationRule;

class RequestItem extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'category_id' => [
                'required',
                'integer',
            ],
            'name' => [
                'required',
                'string',
                'max:255',
                Rule::unique('items', 'name'),
            ],
            'description' => [
                'required',
                'string',
                'max:255',
            ],
            'history' => [
                'string',
                'max:2500',
            ],
            'images' => [
                'required',
                'array',
                'min:1',
                'max:10',
            ],
        ];
    }
}

Answer

I believe the confusing is you are creating a fake file, but actually properly saving it. $image->store('item-images'); doesn’t specify the store you are saving it in, but the ‘item-images’ is the path and it will save it to the default storage. So either Storage::fake() the default storage option.

// assuming default storage is local
Storage::fake('local');

Or change your logic to specify the correct faked storage disk.

Storage::disk('item-images')->putFile('path/yourfile', $image);