dongzi1397 2015-06-30 11:02
浏览 93
已采纳

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

I have a PHPUnit test case, in which I am puzzled by the following snippet. I want to check that the method actionUpload calls the function exposeAndSaveDataLines correctly, i.e. that the first argument is an array as I expect it to be.

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();
}

The expected data is a printout of the current array, made with a temporary print_r (var_export($lines)) in the actual code. I return it in the file expectedLines.php, and when I manually print it, it is correct.

Now, when I run the test case with a single character deliberately misspelled in expectedLines, I get the following error (as expected).

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}'
         )
     )
 )

However, when I correct the mistake, it still mentions that the two arrays are not equal. However, it now prints the entire array (at least the start of it, it is a long array), but it doesn't show any differences (no - and + in front of any line). Why does the expects method not recognize that the two arrays are the same? How am I able to test this properly?

EDIT 1

I have shortened the array, such that it prints the entire array when they are not equal. Still no + or - signs in the comparison.

This is the end of my expectation PHP file.

    '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}'
        )
    )
);

This is the end of my comparison output in the console.

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

I find it suspicious that the last Array is not fully shown in the comparison.

EDIT 2

I find here that the order of the arrays is important. I am pretty sure though I have all elements in the same order, if PHP is not doing something secret under the hood. The solution mentioned there I cannot copy, since I don't have a $this->assertEquals but a ->with($this->equalTo syntax.

EDIT 3

I read here about an undocumented parameter $canonicalize that orders arrays before comparing. When I use it like this:

$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());

I see that the order of the arrays is indeed changed, but I still see the same error. Also, still one array is 'collapsed', which I suspect causes this failure. Besides, I don't want to order all my subarrays, they should be in the same order in the real and expected result.

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

EDIT 4

When I use identicalTo instead of equalTo, I get a more elaborate error message, saying that the one array is not identical to the other array, while printing both of them. I copy-pasted them both into a text file, and used the command diff to check for any differences, but there were none. Still, PHPUnit claims that the two arrays are not equal/identical.

EDIT 5

When I use greaterThanOrEqual or even greaterThan instead of equalTo, then the test passes. This does not happen for lessThanOrEqual. This implies that there is a difference between the two arrays.

If I manually change the expected outcome into something with a string that is alphabetically before the correct string, I can lessThan pass as well, but then of course greaterThanOrEqual fails.

EDIT 6

I am getting convinced that the line ending of the strings in my array are making this comparison to fail, which doesn't show up in all comparisons.

I now have the following assertion.

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;
    }
}

With the following result.

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

Type: string

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

Type: string
  • 写回答

1条回答 默认 最新

  • duanjiong5686 2015-07-08 13:08
    关注

    I finally got it to work, although it is a bit of a compromise. I am now removing newlines before I compare the arrays. This cannot be done in the with method, so I have made the following construction.

    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;
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 微信小程序 用oss下载 aliyun-oss-sdk-6.18.0.min client报错
  • ¥15 ArcGIS批量裁剪
  • ¥15 labview程序设计
  • ¥15 为什么在配置Linux系统的时候执行脚本总是出现E: Failed to fetch http:L/cn.archive.ubuntu.com
  • ¥15 Cloudreve保存用户组存储空间大小时报错
  • ¥15 伪标签为什么不能作为弱监督语义分割的结果?
  • ¥15 编一个判断一个区间范围内的数字的个位数的立方和是否等于其本身的程序在输入第1组数据后卡住了(语言-c语言)
  • ¥15 Mac版Fiddler Everywhere4.0.1提示强制更新
  • ¥15 android 集成sentry上报时报错。
  • ¥15 抖音看过的视频,缓存在哪个文件