当数组中元素的顺序不重要甚至可能发生变化时,断言两个对象数组相等的好方法是什么?
当数组中元素的顺序不重要甚至可能发生变化时,断言两个对象数组相等的好方法是什么?
Answers:
最干净的方法是使用新的断言方法扩展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))
。
您可以使用assertEqualsCanonicalizing PHPUnit 7.5中添加的方法。如果使用此方法比较数组,则这些数组将由PHPUnit数组比较器本身进行排序。
代码示例:
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中,可以使用未声明的参数$ canonicalize assertEquals方法。如果传递$ 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
$delta = 0.0, $maxDepth = 10, $canonicalize = true
将参数传递给函数会产生误导-PHP不支持命名参数。这实际上是在设置这三个变量,然后将它们的值立即传递给函数。如果这三个变量已经在本地范围内定义,则将导致问题,因为它们将被覆盖。
$this->assertEquals($array1, $array2, "\$canonicalize = true", 0.0, 10, true);
。我可以使用4行而不是1行,但是我没有这样做。
$canonicalize
将被删除:github.com/sebastianbergmann/phpunit/issues/3342和assertEqualsCanonicalizing()
将取代它。
我的问题是我有2个数组(数组键与我无关,仅与值无关)。
例如我想测试
$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
,则即使数组值不相等,它也会返回空数组。当然,您还应该测试数组大小是否相同。
$a1 = [1,2,3,4,5]; $a2 = [1,3,5]; var_dump (array_diff ($a1, $a2)); var_dump (array_diff ($a2, $a1))
assertEmpty
如果数组不为空,则不会打印该数组,这在调试测试时很不方便。我建议使用:$this->assertSame(array_diff($expected, $actual), array_diff($actual, $expected), $message);
,因为这将以最少的额外代码显示最有用的错误消息。之所以有效,是因为A \ B = B \ A⇔A \ B和B \ A为空⇔A = B
Array to string conversion
尝试将数组强制转换为字符串时,您会收到一条消息。解决此问题的方法是使用implode
另一种可能性:
$arr = array(23, 42, 108);
$exp = array(42, 23, 108);
sort($arr);
sort($exp);
$this->assertEquals(json_encode($exp), json_encode($arr));
assertEquals
顺序无所谓。
$this->assertSame($exp, $arr);
进行类似比较的方法,$this->assertEquals(json_encode($exp), json_encode($arr));
只是区别是我们不必使用json_encode
简单的助手方法
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);
}
使用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)));
即使您不关心订单,也可以考虑以下因素:
尝试:
asort($foo);
asort($bar);
$this->assertEquals($foo, $bar);
我们在测试中使用以下包装器方法:
/**
* 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);
}
给定的解决方案对我来说没有用,因为我希望能够处理多维数组并清楚地知道两个数组之间的区别。
这是我的功能
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("/"));
我编写了一些简单的代码来首先从多维数组中获取所有键:
/**
* 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;
}
然后要测试它们的结构是否相同,无论键的顺序如何:
$expectedKeys = $this->getAllArrayKeys($expectedData);
$actualKeys = $this->getAllArrayKeys($actualData);
$this->assertEmpty(array_diff($expectedKeys, $actualKeys));
高温超导
如果只想测试数组的值,则可以执行以下操作:
$this->assertEquals(array_values($arrayOne), array_values($arrayTwo));
echo("<pre>"); print_r(array_values(array("size" => "XL", "color" => "gold"))); print_r(array_values(array("color" => "gold", "size" => "XL")));
好像您还没有足够的选择,另一种选择是assertArraySubset
结合使用assertCount
和进行断言。因此,您的代码看起来像。
self::assertCount(EXPECTED_NUM_ELEMENT, $array);
self::assertArraySubset(SUBSET, $array);
这样,您是顺序独立的,但仍断言所有元素都存在。
assertArraySubset
该索引的顺序关系,因此将无法正常工作。即self :: assertArraySubset(['a'],['b','a'])将为假,因为[0 => 'a']
不在内部[0 => 'b', 1 => 'a']
assertEquals
如果键的顺序不同,则已经可以解决这个问题。我刚刚测试过。