PHPUnit:断言两个数组是相等的,但是元素的顺序并不重要

当数组中的元素顺序不重要,甚至可能发生变化时,声明两个对象数组是否相等的好方法是什么?

最干净的方法是使用新的断言方法来扩展phpunit。 但是现在有一个更简单的方法。 未经testing的代码,请validation:

在你的应用程序的某个地方

/** * Determine if two associative arrays are similar * * Both arrays must have the same indexes with identical values * without respect to key ordering * * @param array $a * @param array $b * @return bool */ function arrays_are_similar($a, $b) { // if the indexes don't match, return immediately if (count(array_diff_assoc($a, $b))) { return false; } // we know that the indexes, but maybe not values, match. // compare the values between the two arrays foreach($a as $k => $v) { if ($v !== $b[$k]) { return false; } } // we have identical indexes, and no unequal values return true; } 

在你的testing中:

 $this->assertTrue(arrays_are_similar($foo, $bar)); 

assertEquals方法有一个未logging的参数$ canonicalize。 如果使用$ canonicalize = true ,数组将按照PHPUnit数组比较器自身进行sorting。

代码示例:

 class ArraysTest extends PHPUnit_Framework_TestCase { public function testEquality() { $obj1 = $this->getObject(1); $obj2 = $this->getObject(2); $obj3 = $this->getObject(3); $array1 = [$obj1, $obj2, $obj3]; $array2 = [$obj2, $obj1, $obj3]; // Pass $this->assertEquals($array1, $array2, "\$canonicalize = true", $delta = 0.0, $maxDepth = 10, $canonicalize = true); // Fail $this->assertEquals($array1, $array2, "Default behaviour"); } private function getObject($value) { $result = new stdclass(); $result->property = $value; return $result; } } 

最新版本的PHPUnit数组比较器源代码: https : //github.com/sebastianbergmann/comparator/blob/master/src/ArrayComparator.php#L43

我的问题是,我有2个数组(数组键是不相关的我,只是值)。

比如我想testing一下

 $expected = array("0" => "green", "2" => "red", "5" => "blue", "9" => "pink"); 

有相同的内容(顺序与我无关)为

 $actual = array("0" => "pink", "1" => "green", "3" => "yellow", "red", "blue"); 

所以我使用了array_diff 。

最终的结果是(如果数组相等,差异将导致一个空的数组)。 请注意,差异是两种方式计算(谢谢@ beret,@GordonM)

 $this->assertEmpty(array_merge(array_diff($expected, $actual), array_diff($actual, $expected))); 

对于更详细的错误信息(在debugging的时候),你也可以像这样testing(谢谢@DenilsonSá):

 $this->assertSame(array_diff($expected, $actual), array_diff($actual, $expected)); 

与里面臭虫的老版本:

$ this-> assertEmpty(array_diff($ array2,$ array1));

另外一个可能性:

  1. 对两个数组sorting
  2. 将它们转换为一个string
  3. 断言两个string是相等的

 $arr = array(23, 42, 108); $exp = array(42, 23, 108); sort($arr); sort($exp); $this->assertEquals(json_encode($exp), json_encode($arr)); 

简单的辅助方法

 protected function assertEqualsArrays($expected, $actual, $message) { $this->assertTrue(count($expected) == count(array_intersect($expected, $actual)), $message); } 

或者,如果在数组不等时需要更多的debugging信息

 protected function assertEqualsArrays($expected, $actual, $message) { sort($expected); sort($actual); $this->assertEquals($expected, $actual, $message); } 

如果数组是可sorting的,我将在检查相等之前对它们进行sorting。 如果没有,我会把它们转换成某种forms的集合,然后比较这些集合。

我们在testing中使用下面的包装器方法:

 /** * Assert that two arrays are equal. This helper method will sort the two arrays before comparing them if * necessary. This only works for one-dimensional arrays, if you need multi-dimension support, you will * have to iterate through the dimensions yourself. * @param array $expected the expected array * @param array $actual the actual array * @param bool $regard_order whether or not array elements may appear in any order, default is false * @param bool $check_keys whether or not to check the keys in an associative array */ protected function assertArraysEqual(array $expected, array $actual, $regard_order = false, $check_keys = true) { // check length first $this->assertEquals(count($expected), count($actual), 'Failed to assert that two arrays have the same length.'); // sort arrays if order is irrelevant if (!$regard_order) { if ($check_keys) { $this->assertTrue(ksort($expected), 'Failed to sort array.'); $this->assertTrue(ksort($actual), 'Failed to sort array.'); } else { $this->assertTrue(sort($expected), 'Failed to sort array.'); $this->assertTrue(sort($actual), 'Failed to sort array.'); } } $this->assertEquals($expected, $actual); } 

如果按键是相同的,但是失序,这应该解决它。

您只需要按相同的顺序获得密钥并比较结果。

  /** * Assert Array structures are the same * * @param array $expected Expected Array * @param array $actual Actual Array * @param string|null $msg Message to output on failure * * @return bool */ public function assertArrayStructure($expected, $actual, $msg = '') { ksort($expected); ksort($actual); $this->assertSame($expected, $actual, $msg); } 

使用array_diff() :

 $a1 = array(1, 2, 3); $a2 = array(3, 2, 1); // error when arrays don't have the same elements (order doesn't matter): $this->assertEquals(0, count(array_diff($a1, $a2)) + count(array_diff($a2, $a1))); 

或2断言(更容易阅读):

 // error when arrays don't have the same elements (order doesn't matter): $this->assertEquals(0, count(array_diff($a1, $a2))); $this->assertEquals(0, count(array_diff($a2, $a1))); 

给定的解决scheme没有为我做这个工作,因为我想能够处理multidimensional array,并有两个数组之间有什么不同的明确信息。

这是我的function

 public function assertArrayEquals($array1, $array2, $rootPath = array()) { foreach ($array1 as $key => $value) { $this->assertArrayHasKey($key, $array2); if (isset($array2[$key])) { $keyPath = $rootPath; $keyPath[] = $key; if (is_array($value)) { $this->assertArrayEquals($value, $array2[$key], $keyPath); } else { $this->assertEquals($value, $array2[$key], "Failed asserting that `".$array2[$key]."` matches expected `$value` for path `".implode(" > ", $keyPath)."`."); } } } } 

然后使用它

 $this->assertArrayEquals($array1, $array2, array("/")); 

即使您不关心订单,也可能更容易考虑这一点:

尝试:

 asort($foo); asort($bar); $this->assertEquals($foo, $bar); 

我写了一些简单的代码,首先从一个multidimensional array中获取所有的键:

  /** * Returns all keys from arrays with any number of levels * @param array * @return array */ protected function getAllArrayKeys($array) { $keys = array(); foreach ($array as $key => $element) { $keys[] = $key; if (is_array($array[$key])) { $keys = array_merge($keys, $this->getAllArrayKeys($array[$key])); } } return $keys; } 

然后testing它们的结构是否相同,而不pipe键的顺序如何:

  $expectedKeys = $this->getAllArrayKeys($expectedData); $actualKeys = $this->getAllArrayKeys($actualData); $this->assertEmpty(array_diff($expectedKeys, $actualKeys)); 

HTH

另一个选项,就好像你还没有足够的,就是把assertArraySubsetassertCount结合起来做出断言。 所以,你的代码看起来像。

self::assertCount(EXPECTED_NUM_ELEMENT, $array); self::assertArraySubset(SUBSET, $array);

这样,你是独立的秩序,但仍然断言你的所有元素都存在。