Unit testing of file uploads (solved)

Hi Cakers! I thought I would share something I figured out how to do. It took a few hours, so maybe this will save you some time too. And there is very little info on Google on how to do this on any PHP platform.

If you use PHPUnit with CakePHP, you can actually test simulated file uploads very easily. You just have to know a couple tricks, since Cake’s IntegrationTestCase doesn’t support this directly yet.

Since you can’t set “files” with a special IntegrationTestCase function, you have to modify PHP’s $_FILES array directly. And in order to fix IntegrationTestCase’s calculation of the security token, you have to add the serialized version of the $_FILES fields. So, here’s a working example with CakePHP 3.6:

public function testSignupPost()
{
    // Append a bad file (wrong type) by spoofing PHP's files array
    $_FILES = [
        'my-file-upload' => [
            'error'    => UPLOAD_ERR_OK,
            'name'     => 'test.csv',  // WRONG TYPE
            'size'     => 123,
            'tmp_name' => __FILE__,   // use THIS file - a real file
            'type'     => 'text/csv'
        ]
    ];

    $data = [
        // Regular form fields
        'first_name' => 'Alex',
        'last_name'  => 'Smith',

        // Get the IntegrationTestCase security token to match up by adding
        // the $_FILES field(s) in a serialized manner; the values don't matter
        'my-file-upload.error'    => 'a',
        'my-file-upload.name'     => 'a',
        'my-file-upload.size'     => 'a',
        'my-file-upload.tmp_name' => 'a',
        'my-file-upload.type'     => 'a'
    ];

    $this->post(['_name' => 'myNamedRoute'], $data);

    $this->assertResponseOk();
    
    // Do additional testing of your file here (application specific)

    // Then clear it out; you can put this in tearDown() as well
    $_FILES = [];
}

Hope this helps somebody!

2 Likes

Thanks for the post, I was running into the same problem (CakePHP 3.9)

I used the Laminas\Diactoros\UploadedFile class to simulate an uploaded file.

$file = new UploadedFile(…);

Check the doc of the class to get the correct arguments. These are literally the ones provided in your example:
/**
* @param string|resource $streamOrFile
* @param int $size
* @param int $errorStatus
* @param string|null $clientFilename
* @param string|null $clientMediaType
/**