CakePHP 3.7-测试用例文件上传

时间:2019-01-21 11:07:03

标签: cakephp file-upload phpunit cakephp-3.0

如何在CakePHP 3中使用控制器测试用例测试文件上传功能?

我一直遇到PHP认为该文件未实际上传的问题。适用于浏览器测试但不适用于测试用例的验证规则:

                ->add('file', [
                    'is_uploaded_file' => [
                        'rule' => ['uploadedFile', ['optional' => false]],
                        'message' => 'File is no valid uploaded file'
                   ],

我很快发现is_uploaded_filemove_uploaded_file在单元测试中是不可能的。

但是,关于此的大多数主题都是陈旧的,和/或不是专门针对CakePHP的,因此我认为我会发布一个新问题。

2 个答案:

答案 0 :(得分:1)

您不一定需要修改验证规则,或者可以使用实现\Psr\Http\Message\UploadedFileInterface的对象来替代。 CakePHP的默认上载文件验证支持此类对象。

CakePHP需要zendframework/zend-diactoros,因此您可以使用\Zend\Diactoros\UploadedFile并在测试中执行以下操作:

$data = [
    // ...
    'file' => new \Zend\Diactoros\UploadedFile([
        '/path/to/the/temporary/file/on/disk',
        1234, // filesize in bytes
        \UPLOAD_ERR_OK, // upload (error) status
        'filename.jpg', // upload filename
        'image/jpeg' // upload mime type
    ])
];

uploadedFile规则将自动将此类对象视为上载文件。

当然,用于处理文件上传的代码也必须支持该接口,但并没有那么复杂,您只需要确保将常规文件上传数组转换为UploadedFileInterface实现即可,以便您上传处理程序可以提出要求。

当然可以在上传处理程序本身中完成,因此验证将使用常规文件上传数组以及UploadedFile对象。另一种方法是使用beforeMarshal处理程序/事件在创建实体时更早地对其进行转换,类似于以下内容:

public function beforeMarshal(\Cake\Event\Event $event, \ArrayObject $data, \ArrayObject $options)
{
    $file = \Cake\Utility\Hash::get($data, 'file');
    if ($file === null) {
        return;
    }

    if (!($file instanceof \Psr\Http\Message\UploadedFileInterface)) {
        $file = new \Zend\Diactoros\UploadedFile(
            \Cake\Utility\Hash::get($file, 'tmp_name'),
            \Cake\Utility\Hash::get($file, 'size'),
            \Cake\Utility\Hash::get($file, 'error'),
            \Cake\Utility\Hash::get($file, 'name'),
            \Cake\Utility\Hash::get($file, 'type')
        );
        $data['file'] = $file;
    }
}

如果您随后使用\Psr\Http\Message\UploadedFileInterface::moveTo()移动文件,它将在SAPI(基于浏览器)以及非SAPI(CLI)环境中运行:

try {
    $file->moveTo($targetPath);
} catch (\Exception $exception) {
    $entity->setError(
        'file', [__('The file could not be moved to its destination.')]
    );
}

另请参见

答案 1 :(得分:0)

我实际上几乎是在发布后立即发现的。
该解决方案基于https://pierrerambaud.com/blog/php/2012-12-29-testing-upload-file-with-php

因此解决此问题的唯一方法是覆盖两个内置函数:is_uploaded_filemove_uploaded_file

uploadedFile验证规则位于Cake\Validation内部,并且我在表事件中使用了move函数,因此在App\Model\Table内部使用了

我在控制器测试用例的顶部添加了以下内容:

<?php

namespace Cake\Validation;

function is_uploaded_file($filename)
{
    return true;
}

namespace App\Model\Table;

function move_uploaded_file($filename, $destination)
{
    return copy($filename, $destination);
}

namespace App\Test\TestCase\Controller;

use App\Controller\CarsController;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use Cake\Core\Configure;

/**
 * App\Controller\CarsController Test Case
 */
class CarsControllerTest extends BaseTestCase
{

    use IntegrationTestTrait;

    // ...

它有效!