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

talentia's avatar

How to test file uploads to an API using Mockery

I'm using PHPUnit to unit test my API but I'm unsure as to how to test file uploads.

I watched this Laracast lesson which has given me a good insight into how to use Mockery to test file uploading, but my use case is slightly different and I can't get my test to Green.

Here is my current code

And this is the error that I get back:

Mockery\Exception\InvalidCountException: Method move("uploads", "now-test.jpg") from Mockery_0_Symfony_Component_HttpFoundation_File_UploadedFile should be called exactly 1 times but called 0 times.

Where am I going wrong? Anyone with any examples of how you go about testing file uploads to an API? Any help would be appreciated.

0 likes
4 replies
talentia's avatar

Anyone with any experience mocking file uploads? Still haven't found a solution to this :'(

ifpingram's avatar
Level 4

Your expectation is missing the full upload path, you need:

$file->shouldReceive('move')
            ->once()
            ->with(public_path() . '/uploads', 'now-test.jpg');

to match the function:

    $file->move(public_path().'/uploads', $name);

Also don't forget to remove the $this-dump() from the end of the test in order for it to pass.

1 like
talentia's avatar

Wow. Duh!.. Thanks very much! That's that error resolved.

I then got another error, which I traced back to the Request facade via the Input::hasFile('file') method:

Method Mockery_0_Symfony_Component_HttpFoundation_File_UploadedFile::getPath() does not exist on this mock object

I solved it like this, but it doesn't feel right:

$file->shouldReceive('getPath')
         ->once()
         ->andReturn(true);

Doesn't seem right that I "should expect to receive 'getPath'" – a method which is not in my endpoint but buried within the Request facade. Any tips?

ifpingram's avatar

This seems fine to me, although you are right it does seem like a smell, as you shouldn't need to know the internals of the dependencies that you are mocking, if you don't "own" them.

The right thing to do is in fact to mock the Request facade. This however is problematic, like the docs say:

"Note: You should not mock the Request facade."

I tried it anyway, and it is true; don't do it - it just errors. :)

Instead, mock out the Request instance and swap the one in the IoC for the mock, then use the request() helper function instead of the Facade in your code:

<?php

namespace App;

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Mockery as m;

class ApiResourcesControllerTest extends \TestCase
{
    use WithoutMiddleware;

    /** @test */
    function a_user_can_upload_a_course_resource()
    {
        $request = m::mock(Request::class, ['hasFile' => true]);

        $this->app->instance(Request::class, $request);

        $file = m::mock(UploadedFile::class, [
            'getClientOriginalName' => 'test.jpg'
        ]);

        $file->shouldReceive('move')
            ->once()
            ->with(public_path().'/uploads', 'now-test.jpg');

        $file->shouldReceive('getPath')
            ->once()
            ->andReturn(true);

        $files = ['file' => $file];

        $this->call('POST', 'api/resources/upload', [], [], $files);

        $this->assertEquals(['file_name' => 'now-test.jpg'], json_decode($this->response->getContent(), true));
    }
}

function time()
{
    return 'now';
}

And code:

<?php

namespace App;

class Upload
{
    /**
     * Upload a file
     * POST /api/resources/upload
     *
     * @return Response
     */
    public function uploadFile()
    {
        if (!request()->hasFile('file')) {
            return $this->respondWithError( trans('api_responses.files.none_submitted') );
        }

        $file = request()->file('file');
        $name = time() . '-' . $file->getClientOriginalName();

        $file->move(public_path().'/uploads', $name);

        return $this->respondWithSuccess([
            'file_name' => $name
        ]);
    }

    private function respondWithSuccess($file)
    {
        return $file;
    }
}

My test route is:

Route::post('api/resources/upload', function () {
    $upload = new Upload;
    return $upload->uploadFile();
});

Good luck!

1 like

Please or to participate in this conversation.