CakePHP 3.7 - Загрузка файла тестового примера - PullRequest
0 голосов
/ 21 января 2019

Как я могу проверить функцию загрузки файла с помощью контрольного теста контроллера в CakePHP 3?

Я продолжаю сталкиваться с проблемой, что PHP думает, что файл на самом деле не был загружен. Правила проверки, которые работают для теста браузера, но не для теста:

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

Я быстро обнаружил, что is_uploaded_file и move_uploaded_file невозможно обмануть в модульном тесте.

Тем не менее, большинство тем по этому вопросу старые и / или не относящиеся к CakePHP, поэтому я решил опубликовать новый вопрос.

Ответы [ 2 ]

0 голосов
/ 21 января 2019

Вам не обязательно изменять правила проверки, в качестве альтернативы вы можете использовать объект, реализующий \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.')]
    );
}

Смотри также

0 голосов
/ 21 января 2019

Я на самом деле понял это почти сразу после публикации.
Решение основано на https://pierrerambaud.com/blog/php/2012-12-29-testing-upload-file-with-php

Так что единственный способ обойти проблему - переопределить обе встроенные функции: is_uploaded_file и move_uploaded_file.

Правило проверки uploadedFile находится внутри Cake\Validation, и я использую функцию перемещения в событии таблицы, поэтому внутри 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;

    // ...

И это работает!

...