CakePHP で uploadedFile の Validation をテストする #cakephp #php

概要

CakePHP にはアップロードファイル用の validator が用意されていて便利。

ただテストをするときにちょっとめんどくさい点が2つある。

1つ目はファイルをアップロードするリクエストを作ること。

リクエストするデータとは別に $_FILES にも情報を突っ込んでおかないと、Controller で \Cake\Http\ServerRequest::getUploadedFile あたりを呼び出した時に値が取れなくなる。

もう1つは is_uploaded_file を stub にする必要があること。

uploadedFile の Validation はアップロードファイル判定に is_uploaded_file を使っており、テスト時にこの判定をくぐり抜けるのが難しい。

uploadedFile の Validation

Controller 内で↓のように書ける。便利。

$validator = new Validator();
$validator->uploadedFile(
  'my_file',
  ['types' => ['text/plain']], 
  'ファイル形式が正しくありません'
);
$errors = $validator->errors($this->request->getData());

オプションは mime type 以外にも、サイズ判定などがある。

テストで uploadedFile のリクエストを作る

uploadedFile を Controller のテスト (IntegrationTestTrait) で使う場合、 \Psr\Http\Message\UploadedFileInterface の実装インスタンスを生成してリクエストに乗せる。

\Zend\Diactoros\UploadedFile を使えばいいと思う。

このとき、リクエストデータとは別に $_FILES にもセットしておく必要がある。

直接代入してもいいのだが、 \Cake\TestSuite\IntegrationTestTrait::configRequestfiles いうキーで渡してあげると、結果的に同じことができるようだ。

$testFile = TESTS . 'Fixture' . DS . 'files' . DS . 'my_file.txt';
$uploadedFile = new UploadedFile(
    $testFile,
    10,
    UPLOAD_ERR_OK,
    'my_file.txt',
    'application/octet-stream'
);

// 別途設定する
$this->configRequest(
    [
        'files' => [
            'my_file'=> [
                'error' => $uploadedFile->getError(),
                'name' => $uploadedFile->getClientFilename(),
                'size' => $uploadedFile->getSize(),
                'tmp_name' => $testFile,
                'type' => $uploadedFile->getClientMediaType(),
            ]
        ]
    ]
);

// リクエスト
$this->post("your/api/", ['my_file' => $uploadedFile]);

ちなみにファイルの中身の確認は ext/finfo で 'tmp_name' に対して行われる。

実ファイルに対してチェックが行われるので、配置しておく必要がある。

is_uploaded_file を stub 化する

\Cake\Validation\Validation::uploadedFile が is_uploaded_file を使用しており、どうにも validation をテストで通過できなかったが、 stub 化することで対応できた。

このやり方は CakePHP 本体が同じことをやってる。

stub 用のファイルを用意して、テスト側で読み込めばOK。

// stub 側はこんな感じ
namespace Cake\Validation;

function is_uploaded_file($filename)
{
    return file_exists($filename);
}
// テスト側で stub を読み込む
require_once __DIR__ . DS . 'stubs.php';

参考