当数组元素的顺序无关紧要,甚至可能会发生改变时,断言两个对象数组相等的好方法是什么?
当数组元素的顺序无关紧要,甚至可能会发生改变时,断言两个对象数组相等的好方法是什么?
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->assertEqualsCanonicalizing($array1, $array2);
// Fail
$this->assertEquals($array1, $array2);
}
private function getObject($value)
{
$result = new \stdClass();
$result->property = $value;
return $result;
}
}
在旧版本的PHPUnit中,您可以使用assertEquals方法的一个未记录参数$canonicalize。如果您传递$canonicalize = true,您将获得相同的效果:
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", 0.0, 10, 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#L46
$canonicalize
将被移除:https://github.com/sebastianbergmann/phpunit/issues/3342,`assertEqualsCanonicalizing()`将取而代之。 - koen我的问题是我有两个数组(对我来说,数组键不相关,只关心值)。
例如,我想测试是否
$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)));
如果要获得更加详细的错误信息(用于调试),你可以像这样测试(感谢@DenilsonSá):
$this->assertSame(array_diff($expected, $actual), array_diff($actual, $expected));
有缺陷的旧版本:
$this->assertEmpty(array_diff($array2, $array1));
$array1
比$array2
具有更多的值,则即使数组值不相等,它也会返回空数组。您还应该测试数组大小是否相同,以确保一致。 - petrkotek$a1 = [1,2,3,4,5]; $a2 = [1,3,5]; var_dump(array_diff($a1, $a2)); var_dump(array_diff($a2, $a1))
- GordonMassertEmpty
不会打印出来,这在调试测试时非常不方便。我建议使用 $this->assertSame(array_diff($expected, $actual), array_diff($actual, $expected), $message);
,因为这将以最少的额外代码打印出最有用的错误信息。这是有效的,因为 A\B = B\A ⇔ A\B 和 B\A 都为空 ⇔ A = B。 - Denilson Sá Maia最干净的方法是使用一个新的断言方法扩展phpunit。但现在有一个更简单的想法。以下是未经测试的代码,请验证:
在您的应用程序中的某个地方:
/**
* 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;
}
$this->assertTrue(arrays_are_similar($foo, $bar));
count(array_diff_assoc($b, $a))
。 - JohnSmith另一种可能性:
$arr = array(23, 42, 108);
$exp = array(42, 23, 108);
sort($arr);
sort($exp);
$this->assertEquals(json_encode($exp), json_encode($arr));
assertEquals
,顺序并不重要。 - Wilt$this->assertSame($exp, $arr);
进行比较,它与$this->assertEquals(json_encode($exp), json_encode($arr));
的比较类似,唯一的区别是我们不必使用json_encode。 - maxwells简单的帮助方法
protected function assertEqualsArrays($expected, $actual, $message) {
$this->assertTrue(count($expected) == count(array_intersect($expected, $actual)), $message);
}
或者如果你需要在数组不相等时获取更多的调试信息
protected function assertEqualsArrays($expected, $actual, $message) {
sort($expected);
sort($actual);
$this->assertEquals($expected, $actual, $message);
}
count($actual)
相匹配,否则 assertEqualsArrays([], [1, 2, 3])
将返回 true
。 - Joseph Silber如果键相同但顺序不同,这个方法可以解决问题。
只需要按照相同的顺序获取键并比较结果即可。
/**
* 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);
}
asort($foo);
asort($bar);
$this->assertEquals($foo, $bar);
$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)));
// 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)));
我们在测试中使用以下封装方法:
/**
* 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);
}