pazitron's avatar

Problem with UploadedFile::fake() and PHPUnit test - Unable to find a file at path

I keep getting "Unable to find a file at path [photoA.jpg]. Failed asserting that false is true." when trying to test image upload.

my controller store method:

public function store(Request $request)
    {
        $validatedData = $request->validate([
            'title' => 'required | max:100',
            'description' => 'required',
            'image' => 'required | image | max:1000',
            'excerpt' => 'required | max:200'
        ]);

        $post = Post::create([
            'title' => request('title'),
            'description' => request('description'),
            'image' => $request->file('image')->store('images/blog_posts', 'public'),
            'excerpt' => request('excerpt')
        ]);
        return redirect()->route('manage.posts.show', $post)
                        ->with('success', 'Your post has been successfully created!');
    }

my test:

public function it_can_upload_post_image()
    {
        Storage::fake('local');

        $manager = factory(User::class)
                    ->states('manager')
                    ->create();

        $this->actingAs($manager)->post(route('manage.posts.store'), [
            'title' => 'Post title A',
            'slug' => 'post-title-a',
            'image' => UploadedFile::fake()->image('photoA.jpg'),
            'excerpt' => 'Excerpt for post A',
            'description' => 'Description for post A'
        ]);
        Storage::disk('local')->assertExists('photoA.jpg');
    }

what am I missing? Thank you in advance!

0 likes
8 replies
Talinon's avatar

@pazitron Within your controller, you have it saving to /images/blog_posts on your public disk, but in your test, you have it asserting against the root path of your local disk.

pazitron's avatar

Good spot! Though after amending my test method, I still get the same "Unable to find a file...". The amended test now looks like this:

public function it_can_upload_post_image()
    {
        Storage::fake('public');

        $manager = factory(User::class)
                    ->states('manager')
                    ->create();

        $this->actingAs($manager)->post(route('manage.posts.store'), [
            'title' => 'Post title A',
            'slug' => 'post-title-a',
            'image' => UploadedFile::fake()->image('photoA.jpg'),
            'excerpt' => 'Excerpt for post A',
            'description' => 'Description for post A'
        ]);
        Storage::disk('public')->assertExists('photoA.jpg');
    }
Talinon's avatar

@pazitron You're still not asserting against the same path as you're storing the file within your controller. You're asserting that photoA.jpg exists in the root directory of your public disk, while in your controller you're storing it under /images/blog_posts

pazitron's avatar

@talinon that's the bit I got confused with as I also tried the following assertion unsuccessfully:

Storage::disk('public')->assertExists('/images/blog_posts/photoA.jpg');
Talinon's avatar

@pazitron Confirm to see if the file is being stored. A temporary file should be created in your storage/framework/testing/disks/public/images/blog_posts/photoA.jpg after you run the test.

According to the documentation:

Note that we only specified a directory name, not a file name. By default, the store method will generate a unique ID to serve as the file name.

So it might be that it's storing it as a random file name, which is why its failing against photoA.. you could try using storeAs() to be specific about the file name

Also make sure you have enctype="multipart/form-data" on your form tag within your markup.

pazitron's avatar

I can see a file being created under testing/disks/... folder when I run the test. The storeAs() you mentioned, can I chain it to the UploadedFile::fake()->image()?

Talinon's avatar
Talinon
Best Answer
Level 51

@pazitron You don't need to chain it there.. the test is only making the fake file to include within the POST request. You only need to update your controller with storeAs() to be specific about the name. I've never used just store(), I've always used storeAs() so I can sanitize the filename. You can get the filename within your controller using request()->file('image')->getClientOriginalName() then do whatever you want with the string before passing it into the storeAs() method.

pazitron's avatar

Thanks @talinon ! Apparently there is no obvious way of passing the test with store() unless I change the phpunit.xml to set the storage disk used for testing from framework/testing to the app/public. Seems like a bit of a flaw, there must be a better way of running tests and using the store() method.

Please or to participate in this conversation.