如何使用二进制数据填充php://input
以测试上传? (或以其他方式测试分块上传)。我使用plupload作为我的前端,我想对我的后端进行单元测试。
这是我要测试的代码段:
public function recieve($file = 'file')
{
// Get parameters
$chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
$chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;
$fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : '';
$targetDir = $this->_uploadDir;
// Clean the fileName for security reasons
$fileName = preg_replace('/[^\w\._]+/', '_', $fileName);
// Make sure the fileName is unique but only if chunking is disabled
if ($chunks < 2 && file_exists($targetDir . DIRECTORY_SEPARATOR . $fileName)) {
$ext = strrpos($fileName, '.');
$fileName_a = substr($fileName, 0, $ext);
$fileName_b = substr($fileName, $ext);
$count = 1;
while (file_exists(
$targetDir . DIRECTORY_SEPARATOR . $fileName_a . '_' . $count . $fileName_b)) {
$count++;
}
$fileName = $fileName_a . '_' . $count . $fileName_b;
}
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
// Create target dir
if (!file_exists($targetDir)) {
if (!is_writable(dirname($targetDir))) {
$this->_messages[] = 'Cannot write to ' . dirname($targetDir) . ' for mkdir';
return false;
}
mkdir($targetDir, 0777, true);
}
// Check permissions
if (!is_writable($targetDir)) {
$this->_messages[] = 'Unable to write to temp directory.';
return false;
}
// Look for the content type header
$contentType = null;
if (isset($_SERVER["HTTP_CONTENT_TYPE"]))
$contentType = $_SERVER["HTTP_CONTENT_TYPE"];
if (isset($_SERVER["CONTENT_TYPE"]))
$contentType = $_SERVER["CONTENT_TYPE"];
// Handle non multipart uploads older WebKit versions didn't support multipart in HTML5
if (strpos($contentType, "multipart") !== false) {
if (isset($_FILES[$file]['tmp_name']) && is_uploaded_file($_FILES[$file]['tmp_name'])) {
// Open temp file
$out = fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab");
if ($out) {
// Read binary input stream and append it to temp file
$in = fopen($_FILES[$file]['tmp_name'], "rb");
if ($in) {
while ($buff = fread($in, 4096)) {
fwrite($out, $buff);
}
} else {
$this->_messages[] = 'Failed to open input stream.';
return false;
}
fclose($in);
fclose($out);
unlink($_FILES[$file]['tmp_name']);
} else {
$this->_messages[] = 'Failed to open output stream.';
return false;
}
} else {
$this->_messages[] = 'Failed to move uploaded file.';
return false;
}
} else {
// Open temp file
$out = fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab");
if ($out) {
// Read binary input stream and append it to temp file
$in = fopen("php://input", "rb");
if ($in) {
while ($buff = fread($in, 4096)) {
fwrite($out, $buff);
}
} else {
$this->_messages[] = 'Failed to open input stream.';
return false;
}
fclose($in);
fclose($out);
} else {
$this->_messages[] = 'Failed to open output stream.';
return false;
}
}
// Check if file upload is complete
if (!$chunks || $chunk == $chunks - 1) {
// Strip the temp .part suffix off
rename("{$filePath}.part", $filePath);
return $filePath;
}
}
*修改:
添加了更多代码,以显示我想要进行单元测试的内容
答案 0 :(得分:6)
似乎这不能通过常规的PHPUnit测试来完成,但我找到了一种方法来将.phpt测试与PHPUnit集成在:http://qafoo.com/blog/013_testing_file_uploads_with_php.html
供参考,uploadTest.phpt:
--TEST--
Example test emulating a file upload
--POST_RAW--
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryfywL8UCjFtqUBTQn
------WebKitFormBoundaryfywL8UCjFtqUBTQn
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain
Contents of text file here
------WebKitFormBoundaryfywL8UCjFtqUBTQn
Content-Disposition: form-data; name="submit"
Upload
------WebKitFormBoundaryfywL8UCjFtqUBTQn--
--FILE--
<?php
require __DIR__ . '/Upload.php';
$upload = new Upload();
$file = $upload->recieve('file');
var_dump(file_exists($file));
?>
--EXPECT--
bool(true)
相应的PHPUnit测试集成:
<?php
require_once 'PHPUnit/Extensions/PhptTestCase.php';
class UploadExampleTest extends PHPUnit_Extensions_PhptTestCase
{
public function __construct()
{
parent::__construct(__DIR__ . '/uploadTest.phpt');
}
}
答案 1 :(得分:2)
首先,如果不是单一的200行方法,你会发现这个代码更容易进行单元测试!单位越小 - 测试越小。您可以提取getFileName()
,getContentType()
,isChunked()
或getChunkDetails()
,transferChunk()
等。其中许多方法都很短,可让您彻底测试无需设置整个上传。这是一个例子,getContentType()
:
public function getContentType() {
if (isset($_SERVER["CONTENT_TYPE"]))
return $_SERVER["CONTENT_TYPE"];
if (isset($_SERVER["HTTP_CONTENT_TYPE"]))
return $_SERVER["HTTP_CONTENT_TYPE"];
throw new FileTransferException('Unknown content type');
}
这种方法的测试很简单。
/**
* @expectedException FileTransferException
*/
public function testUnknownContentType() {
$fixture = new FileTransfer();
unset($_SERVER["CONTENT_TYPE"]);
unset($_SERVER["HTTP_CONTENT_TYPE"]);
$fixture->getContentType();
}
public function testRegularContentType() {
$fixture = new FileTransfer();
$_SERVER["CONTENT_TYPE"] = 'regular';
unset($_SERVER["HTTP_CONTENT_TYPE"]);
self::assertEquals('regular', $fixture->getContentType());
}
public function testHttpContentType() {
$fixture = new FileTransfer();
unset($_SERVER["CONTENT_TYPE"]);
$_SERVER["HTTP_CONTENT_TYPE"] = 'http';
self::assertEquals('http', $fixture->getContentType());
}
public function testRegularContentTypeTakesPrecedence() {
$fixture = new FileTransfer();
$_SERVER["HTTP_CONTENT_TYPE"] = 'http';
$_SERVER["CONTENT_TYPE"] = 'regular';
self::assertEquals('regular', $fixture->getContentType());
}
一旦使用简单的东西重构代码,就可以将所有I / O处理提取到一个单独的类中。通过这样做,您可以在测试非I / O代码时使用模拟对象,这意味着您不必依赖实际文件或使用伪数据填充php://input
。这是“单元测试”的“单元”部分:将代码分解为可测试的小单元,并在可行的情况下从等式中删除其他单元。
在提取的I / O处理类中,将调用放到is_uploaded_file()
并将输入流打开到不同的方法中,例如isUploadedFile()
和openInputStream()
。在测试时,您可以模拟这些方法,而不是模拟其底层机制。测试is_uploaded_file()
在单元测试中的作用是没有意义的。这是PHP的责任,您可以在集成(端到端)测试中验证一切是否正常工作。
这将减少对I / O代码的测试。此时,您可以使用测试文件夹中的真实文件或vfsStream等包。