PHPUnit:期望使用数组作为参数进行方法调用

时间:2015-06-30 11:02:31

标签: 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没有做一些秘密的话。那里提到的解决方案我无法复制,因为我没有$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

1 个答案:

答案 0 :(得分:0)

我终于得到了它的工作,虽然这是一个妥协。在比较数组之前,我现在正在删除换行符。这不能在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;
    }
}