vfsstream路径和realpath

时间:2014-03-12 22:48:09

标签: php mocking phpunit vfs-stream

我正在尝试使用vfsStream进行单元测试文件系统交互,并且很快遇到了一个主要障碍。其中一个验证检查测试中的代码是在提供的输入路径上执行realpath()来测试它是一个实际路径而不是无意义。但是,realpath总是在vfsstream路径上失败。

以下代码演示了任何特定类之外的问题。

$content    = "It rubs the lotion on its skin or else it gets the hose again";
$url        = vfsStream::url ('test/text.txt');
file_put_contents ($url, $content);
var_dump ($url);
var_dump (realpath ($url));
var_dump (file_get_contents ($url));

输出如下:

string(27) "vfs://FileClassMap/text.txt"
bool(false)
string(61) "It rubs the lotion on its skin or else it gets the hose again"

显然vfsStream创建了文件并将给定的内容写入其中,但我无法验证它的路径是否与realpath一致。由于在实际代码中使用了realpath,我需要一种解决方法。

我真的不认为删除realpath是一种明智的方法,因为它在代码中执行一个重要的功能,并且消除一个重要的检查只是为了使代码可测试似乎是一个非常糟糕的解决方案。我还可以在测试周围放置一个if来使其可以禁用它用于测试目的,但我不认为这也是一个好主意。此外,我不想在代码中的每一点都这样做,我可能会调用realpath()。第三种选择是为文件系统单元测试设置RAM磁盘,但这也不理想。你必须自己清理(这是vfsstream应该帮助你避免的需要)以及如何实际操作它将因操作系统而异,所以单元测试将不再是操作系统不可知的。

那么有没有办法以实际与realpath一起使用的格式获取vfsstream路径?

为了完整性,以下是我正在尝试实际测试的类中的代码片段。

if (($fullPath = realpath ($unvalidatedPath))
&& (is_file ($fullPath))
&& (is_writable ($fullPath))) {

对以下内容的重构(根据潜在的解决方案2)允许我使用vfsStream进行测试,但我认为它在生产中可能存在问题:

// If we can get a canonical path then do so (realpath can fail on URLs, stream wrappers, etc)
$fullPath   = realpath ($unvalidatedPath);
if (false === $fullPath) {
    $fullPath   = $unvalidatedPath;
}

if ((is_file ($fullPath))
&& (is_writable ($fullPath))) {

2 个答案:

答案 0 :(得分:3)

如果使用名称空间,则只能在测试类中覆盖realpath函数。我总是在我的vfsStream测试用例中使用规范路径,因为我不想测试realpath()函数本身。

namespace my\namespace;

/**
 * Override realpath() in current namespace for testing
 *
 * @param string $path     the file path
 *
 * @return string
 */
function realpath($path)
{
    return $path;
}

这里描述的很好:http://www.schmengler-se.de/en/2011/03/php-mocking-built-in-functions-like-time-in-unit-tests/

答案 1 :(得分:0)

我在vfsStream上使用Sebkrueger的方法的实现打开了一个错误:https://github.com/bovigo/vfsStream/issues/207

等待他们的反馈,这是我正在使用的realpath():

/**
 * This function overrides the native realpath($url) function, removing
 * all the "..", ".", "///" of an url. Contrary to the native one, 
 * 
 * @param string $url
 * @param string|bool The cleaned url or false if it doesn't exist
 */
function realpath(string $url)
{
    preg_match("|^(\w+://)?(/)?(.*)$|", $url, $matches);
    $protocol = $matches[1];
    $root     = $matches[2];
    $rest     = $matches[3];

    $split = preg_split("|/|", $rest);

    $cleaned = [];
    foreach ($split as $item) {
        if ($item === '.' || $item === '') {
            // If it's a ./ then it's nothing (just that dir) so don't add/delete anything
        } elseif ($item === '..') {
            // Remove the last item added since .. negates it.
            $removed = array_pop($cleaned);
        } else {
            $cleaned[] = $item;
        }
    }

    $cleaned = $protocol.$root.implode('/', $cleaned);
    return file_exists($cleaned) ? $cleaned : false;
}