Zaher Ghaibeh
PHP Backend developer
I've experience in a few PHP Frameworks, such as Laravel, Lumen and Slim (The last two are used for building Microservices/API services).
Testing upload file with Slim 3
Published at Sunday, June 18, 2017 , Categorized under: Code, PHP

This may not be the correct way, but sadly enough I didnt find any resources about this topic, most articles talk about Laravel, so if you know better way please write it in the comments. Thanks

Automatic testing may not the best topic I can talk about, at least for now, but it's something that am committing to learn. So yesterday I was playing with Slim 3 (which is a PHP microframework) and I needed to try and see if I can test a dummy controller which upload a file and store it.

I went to the mighty and only one Google, but most of the articles was about Laravel or how you can mock the file system with vfsStream, and that's was not what I want, so after playing for few hours I got through 90% of it, as I still need to test the existing of the uploaded file.

So here are my notes, and how I did it:

1- You will need to modify the base test file (BaseTestCase.php), so you can pass to it the uploaded file.

    /**
     * Process the application given a request method and URI
     *
     * @param string $requestMethod the request method (e.g. GET, POST, etc.)
     * @param string $requestUri the request URI
     * @param array|object|null $requestData the request data
     * @param array|null $files the files that we want to upload
     * @return Response
     */
    public function runApp($requestMethod, $requestUri, $requestData = null, $files = null)
    {
        // Create a mock environment for testing with
        $environment = Environment::mock(

                'REQUEST_METHOD' => $requestMethod,
                'REQUEST_URI' => $requestUri,
                'slim.files' => $files // this was missing from the original code

        );
        $request = Request::createFromEnvironment($environment);

        if (null !== $requestData) {
            $request = $request->withParsedBody($requestData);
        }
        $response = new Response();
        $settings = require __DIR__ . '/../../src/settings.php';
        $app = new App($settings);
        require __DIR__ . '/../../src/dependencies.php';
        if ($this->withMiddleware) {
            require __DIR__ . '/../../src/middleware.php';
        }
        require __DIR__ . '/../../src/routes.php';
        return $app->process($request, $response);

    }

2- You will need to install Mockery, so you can mock the most important file, the UploadedFile.php  (note to myself: check the tests on the framework itself). You will need to add the following code either to your test code or to BaseTestCase.php file, I added it to BaseTestCase.php:

    public function tearDown()
    {
        Mockery::close();
    }

3- You will need to mock each and every function you will use, for example, you will use the following:

  • getError
  • getClientFilename
  • moveTo
    /**
     * Test the upload to local route
     */
    public function testUploadToLocal()
    {
        $file = Mockery::mock(UploadedFile::class, 'file.jpg', 'file.jpg', 'image/jpeg', 10000, 0);

        $file->shouldReceive('getError')->andReturn(0)->once();

        $file->shouldReceive('getClientFilename')->andReturn('file.jpg')->once();

        $file->shouldReceive('moveTo')->once();

        $response = $this->runApp('POST', '/upload', null, 
            'file' => $file
        );

        $this->assertEquals(200, $response->getStatusCode());
    }

The constructor for UploadedFile class takes 5 parameters, they are:

  • The file.
  • The file name.
  • The file type.
  • The size of the file.
  • Finally the error code.

For now, this test just passes phoney values, and it will give you green value, but it is still missing the check if the file exists in the uploaded directory or not, I think this is where vfsStream is used so I'll leave it for later. but if you want to test the function with a real file, feel free and you can do it like this

    public function testUploadRealFileToLocal()
    {
        $file = Mockery::mock(UploadedFile::class, 
            file_get_contents(__DIR__.'/../_Files/4zJYinT - Imgur.jpg'),
            '4zJYinT - Imgur.jpg', 'image/jpeg', 10000, 0);

        $file->shouldReceive('getError')->andReturn(0)->once();

        $file->shouldReceive('getClientFilename')->andReturn('4zJYinT - Imgur.jpg')->once();

        $file->shouldReceive('moveTo')->once();

        $response = $this->runApp('POST', '/upload/cloud', null, 
            'file' => $file
        );

        $this->assertEquals(200, $response->getStatusCode());
    }

as you noticed, I passed the content of a real file to the function, but again it is still missing the check if the file exists after upload or not. But for me so far this was a great achievement and I wanted to share it.

Now this might be good for now, but sure enough, I still need to read and learn more about this and how can I improve it more and take it with me to the next level