PHPUnit:期望以数组作为参数的方法调用

标签 php arrays mocking phpunit

我有一个 PHPUnit 测试用例,其中我对以下代码片段感到困惑。我想检查方法 actionUpload 是否正确调用了函数 exposeAndSaveDataLines,即第一个参数是我期望的数组。

public function test_actionUpload()
{
    $sut = $this->getMockBuilder('MasterdataController')
                ->setMethods(array('exposeAndSaveDataLines', 'render'))
                ->disableOriginalConstructor()
                ->getMock();

    $expectedLines = require_once ($this->dataDir . 'expectedLines.php');

    $sut->expects($this->once())
        ->method('exposeAndSaveDataLines')
        ->with($this->equalTo($expectedLines),
            $this->anything(),
            $this->anything(),
            $this->anything(),
            $this->anything());

    $sut->actionUpload();
}

预期数据是当前数组的打印输出,在实际代码中使用临时 print_r (var_export($lines)) 制作。我在文件 expectedLines.php 中返回它,当我手动打印它时,它是正确的。

现在,当我在 expectedLines 中使用故意拼错的单个字符运行测试用例时,我收到以下错误(如预期的那样)。

Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
             3 => 'Colour Group Code'
-            4 => '{2F30E832-D3DB-447E-B733-7BC5125CBCCc}'
+            4 => '{2F30E832-D3DB-447E-B733-7BC5125CBCCC}'
         )
     )
 )

但是我改错的时候还是提示两个数组不相等。但是,它现在打印整个数组(至少它的开头,它是一个长数组),但它没有显示任何差异(任何行前面没有 - 和 +)。为什么 expects 方法无法识别这两个数组相同?我如何才能正确测试它?

编辑 1

我缩短了数组,以便在它们不相等时打印整个数组。比较中仍然没有 + 或 - 符号。

这是我期望的 PHP 文件的结尾。

    'RetTarget Area' => array(
        0 => array(
            0 => '',
            1 => '',
            2 => '{C19D52BC-834C-45DA-B17F-74D73A2EC0BB}
'
        ),
        1 => array(
            0 => '1',
            1 => '1',
            2 => '{5E25C44F-C18A-4F54-B6B1-248955A82E59}'
        )
    )
);

我在控制台中的比较输出到此结束。

     'RetTarget Area' => Array (
         0 => Array (
             0 => ''
             1 => ''
             2 => '{C19D52BC-834C-45DA-B17F-74D73A2EC0BB}
             '
         )
         1 => Array (...)
     )
 )

我怀疑最后一个数组没有在比较中完全显示。

编辑 2

我找到了 here数组的顺序很重要。我很确定虽然我的所有元素都按相同的顺序排列,但如果 PHP 没有在幕后做一些 secret 的事情。那里提到的解决方案我无法复制,因为我没有 $this->assertEquals 而是 ->with($this->equalTo 语法。

编辑 3

我读了here关于在比较之前对数组进行排序的未记录参数 $canonicalize。当我这样使用它时:

$sut->expects($this->once())
    ->method('exposeAndSaveDataLines')
    ->with($this->equalTo($expectedLines, $delta = 0.0, $maxDepth = 10, $canonicalize = true, $ignoreCase = false),
        $this->anything(),
        $this->anything(),
        $this->anything(),
        $this->anything());

我看到数组的顺序确实改变了,但我仍然看到同样的错误。此外,还有一个阵列“折叠”,我怀疑这是导致此故障的原因。此外,我不想对我所有的子数组进行排序,它们在实际结果和预期结果中的顺序应该相同。

--- Expected
+++ Actual
@@ @@
 Array (
     0 => Array (
         0 => Array (
             0 => ''
             1 => ''
             2 => '{C19D52BC-834C-45DA-B17F-74D73A2EC0BB}
             '
         )
         1 => Array (...)
     )

编辑 4

当我使用 identicalTo 而不是 equalTo 时,我得到了一个更详细的错误消息,说一个数组与另一个数组不相同,同时打印两个他们。我将它们都复制粘贴到一个文本文件中,并使用 diff 命令检查是否有任何差异,但没有发现任何差异。不过,PHPUnit 声称这两个数组不相等/不相同。

编辑 5

当我使用 greaterThanOrEqual 甚至 greaterThan 而不是 equalTo 时,测试通过。 lessThanOrEqual 不会发生这种情况。这意味着这两个数组之间 存在差异。

如果我手动将预期结果更改为按字母顺序排列在正确字符串之前的字符串,我也可以 lessThan 通过,但当然 greaterThanOrEqual 会失败.

编辑 6

我越来越确信我的数组中字符串的行结尾导致比较失败,这并没有出现在所有比较中。

我现在有以下断言。

public function test_actionUpload_v10MasterdataFile()
{
    ....
    $sut->expects($this->once())
        ->method('exposeAndSaveDataLines')
        ->will($this->returnCallback(function($lines) {
            $expectedLines = include ($this->dataDir . 'ExpectedLines.php');
            $arrays_similar = $this->similar_arrays($lines, $expectedLines);
            PHPUnit_Framework_Assert::assertTrue($arrays_similar);
        }));
    $sut->actionUpload();
}

private function similar_arrays($a, $b)
{
    if(is_array($a) && is_array($b))
    {
        if(count(array_diff(array_keys($a), array_keys($b))) > 0)
        {
            print_r(array_diff(array_keys($a), array_keys($b)));
            return false;
        }

        foreach($a as $k => $v)
        {
            if(!$this->similar_arrays($v, $b[$k]))
            {
                return false;
            }
        }

        return true;
    }
    else
    {
        if ($a !== $b)
        {
            print_r(PHP_EOL . 'A: '. $a. PHP_EOL . 'Type: ' . gettype($a) . PHP_EOL);
            print_r(PHP_EOL . 'B: '. $b. PHP_EOL . 'Type: ' . gettype($b) . PHP_EOL);
        }
        return $a === $b;
    }
}

结果如下。

A: {72C2F175-9F50-4C9C-AF82-9E3FB875EA82}

Type: string

B: {72C2F175-9F50-4C9C-AF82-9E3FB875EA82}

Type: string

最佳答案

我终于让它工作了,尽管这有点妥协。我现在在比较数组之前删除换行符。这在with方法中是做不到的,所以做了如下构造。

public function test_actionUpload_v10MasterdataFile()
{
    /*
     * Create a stub to disable the original constructor.
     * Exposing data and rendering are stubbed.
     * All other methods behave exactly the same as in the real Controller.
     */
    $sut = $this->getMockBuilder('MasterdataController')
                ->setMethods(array('exposeAndSaveDataLines', 'render'))
                ->disableOriginalConstructor()
                ->getMock();

    $sut->expects($this->once())
        ->method('exposeAndSaveDataLines')
        ->will($this->returnCallback(function($lines) {
            $expectedLines = include ($this->dataDir . 'ExpectedLines.php');
            PHPUnit_Framework_Assert::assertTrue($this->similar_arrays($lines, $expectedLines));
        }));

    // Execute the test
    $sut->actionUpload();
}

...

private function similar_arrays($a, $b)
{
    /**
     * Check if two arrays have equal keys and values associated with it, without
     * looking at order of elements, and discarding newlines.
     */
    if(is_array($a) && is_array($b))
    {
        if(count(array_diff(array_keys($a), array_keys($b))) > 0)
        {
            return false;
        }

        foreach($a as $k => $v)
        {
            if(!$this->similar_arrays($v, $b[$k]))
            {
                return false;
            }
        }
        return true;
    }
    else
    {
        $a = rtrim($a);
        $b = rtrim($b);
        $extended_output = false;
        if ($extended_output && ($a !== $b))
        {
            print_r(PHP_EOL . 'A: '. $a. PHP_EOL . 'Type: ' . gettype($a) . PHP_EOL);
            print_r(PHP_EOL . 'B: '. $b. PHP_EOL . 'Type: ' . gettype($b) . PHP_EOL);
        }
        return $a === $b;
    }
}

关于PHPUnit:期望以数组作为参数的方法调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31136497/

相关文章:

PHP - 从面包屑列表的多维关联数组返回父数组

database - 在没有库的情况下如何模拟数据库调用?

unit-testing - 带有模拟的 Angular 2 TestBed

php - 生成图像然后发送它而不先将其保存为文件?

php - 在 WordPress 数据库上运行查询并根据登录用户的自定义元返回值

php - 实现异地备份 php/mysql 的最佳方式

Javascript 将具有相同起始字符的数组中的单词分组

php - 获取 MySQL 数据库中的项目数

c++ - 对数组的引用参数有什么用?

python - 类型错误 : argument of type 'Mock' is not iterable