CakePHP 3.7 - 测试用例文件上传
CakePHP 3.7 - Test case file upload
如何在 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
在单元测试中是不可能被骗的。
但是,关于这个的大多数主题都是旧的and/or,与 CakePHP 无关,所以我想 post 一个新问题。
实际上我在发布后几乎立刻就明白了。
解决方案基于https://pierrerambaud.com/blog/php/2012-12-29-testing-upload-file-with-php
所以解决这个问题的唯一方法是覆盖两个内置函数:is_uploaded_file
和 move_uploaded_file
。
uploadedFile
验证规则位于 Cake\Validation
内,我在 table 事件中使用移动函数,所以在 App\Model\Table
.[=17= 内]
我在控制器测试用例的顶部添加了以下内容:
<?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;
// ...
而且有效!
您不一定需要修改验证规则,您可以选择使用实现 \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
handler/event,类似于:
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)) {
if (!is_uploaded_file(\Cake\Utility\Hash::get($file, 'tmp_name'))) {
$file = new \Zend\Diactoros\UploadedFile(
null,
0,
UPLOAD_ERR_NO_FILE,
null,
null
);
} else {
$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;
}
}
这会将数据转换为 UploadedFile
对象,以防它是实际上传的文件。添加此额外检查是因为 CakePHP 将文件数据与 POST 数据合并的行为,使得它不可能(除非可以访问请求对象,或 $_FILES
超全局)确定用户是否发布了该数据,或者 PHP 是否为实际文件上传生成了该数据。
如果您随后使用 \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.')]
);
}
另见
如何在 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
在单元测试中是不可能被骗的。
但是,关于这个的大多数主题都是旧的and/or,与 CakePHP 无关,所以我想 post 一个新问题。
实际上我在发布后几乎立刻就明白了。
解决方案基于https://pierrerambaud.com/blog/php/2012-12-29-testing-upload-file-with-php
所以解决这个问题的唯一方法是覆盖两个内置函数:is_uploaded_file
和 move_uploaded_file
。
uploadedFile
验证规则位于 Cake\Validation
内,我在 table 事件中使用移动函数,所以在 App\Model\Table
.[=17= 内]
我在控制器测试用例的顶部添加了以下内容:
<?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;
// ...
而且有效!
您不一定需要修改验证规则,您可以选择使用实现 \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
handler/event,类似于:
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)) {
if (!is_uploaded_file(\Cake\Utility\Hash::get($file, 'tmp_name'))) {
$file = new \Zend\Diactoros\UploadedFile(
null,
0,
UPLOAD_ERR_NO_FILE,
null,
null
);
} else {
$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;
}
}
这会将数据转换为 UploadedFile
对象,以防它是实际上传的文件。添加此额外检查是因为 CakePHP 将文件数据与 POST 数据合并的行为,使得它不可能(除非可以访问请求对象,或 $_FILES
超全局)确定用户是否发布了该数据,或者 PHP 是否为实际文件上传生成了该数据。
如果您随后使用 \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.')]
);
}
另见