PHPUnit тест загрузки двоичных данных - PullRequest
5 голосов
/ 28 января 2012

Как я могу заполнить 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;
    }
}

* Редактировать :

Добавлен дополнительный код, чтобы показать, что я хочу для модульного теста

Ответы [ 2 ]

6 голосов
/ 29 января 2012

Кажется, что этого нельзя сделать с помощью обычных тестов PHPUnit, но я нашел способ интегрировать тесты .phpt с PHPUnit по адресу: http://qafoo.com/blog/013_testing_file_uploads_with_php.html

*

И соответствующая интеграция теста PHPUnit:

<?php
require_once 'PHPUnit/Extensions/PhptTestCase.php';
class UploadExampleTest extends PHPUnit_Extensions_PhptTestCase
{
    public function __construct()
    {
        parent::__construct(__DIR__ . '/uploadTest.phpt');
    }
}
2 голосов
/ 29 января 2012

Во-первых, вы найдете, что этот код значительно проще для модульного тестирования, если бы это не был метод из 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());
}

Как только вы реорганизуете код с помощью простых вещей, вы можете извлечь всю обработку ввода-вывода в отдельный класс. Таким образом, вы можете использовать фиктивный объект при тестировании кода, не относящегося к вводу-выводу, что означает, что вам не придется полагаться на реальные файлы или заполнять php://input поддельными данными. Это «модульная» часть «модульного тестирования»: разбивка кода на маленькие тестируемые блоки и удаление других блоков из уравнения, где это практически возможно.

В извлеченном классе обработки ввода / вывода поместите вызовы к is_uploaded_file() и откройте входной поток для отдельных методов, например, isUploadedFile() и openInputStream(). Во время тестирования вы можете издеваться над этими методами, а не над их основными механизмами. Нет смысла тестировать, что is_uploaded_file() работает в модульном тесте. Это ответственность PHP, и вы можете проверить, что все работает, как ожидается, в интеграционном (сквозном) тесте.

Это сократит тестирование вашего кода ввода-вывода до минимума. На этом этапе вы можете использовать реальные файлы в папке с тестами или в пакете, например vfsStream .

...