Một cách tốt để khẳng định rằng hai mảng đối tượng là bằng nhau, khi thứ tự của các phần tử trong mảng là không quan trọng, hoặc thậm chí có thể thay đổi?
Một cách tốt để khẳng định rằng hai mảng đối tượng là bằng nhau, khi thứ tự của các phần tử trong mảng là không quan trọng, hoặc thậm chí có thể thay đổi?
Câu trả lời:
Cách sạch nhất để làm điều này là mở rộng phpunit bằng một phương thức khẳng định mới. Nhưng đây là một ý tưởng cho một cách đơn giản hơn bây giờ. Mã chưa được kiểm tra, vui lòng xác minh:
Một nơi nào đó trong ứng dụng của bạn:
/**
* 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;
}
Trong bài kiểm tra của bạn:
$this->assertTrue(arrays_are_similar($foo, $bar));
count(array_diff_assoc($b, $a))
.
Bạn có thể dùng phương thức assertEqualsCanonicalizing đã được thêm vào trong PHPUnit 7.5. Nếu bạn so sánh các mảng bằng phương thức này, các mảng này sẽ được sắp xếp theo chính bộ so sánh mảng PHPUnit.
Mã ví dụ:
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;
}
}
Trong các phiên bản cũ hơn của PHPUnit, bạn có thể sử dụng một tham số chuẩn hóa $ paramicalize của phương thức assertEquals . Nếu bạn vượt qua $ canonicalize = true , bạn sẽ nhận được hiệu ứng tương tự:
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;
}
}
Sắp xếp mã nguồn so sánh ở phiên bản mới nhất của PHPUnit: https://github.com/sebastianbergmann/comparator/blob/master/src/ArrayComparator.php#L46
$delta = 0.0, $maxDepth = 10, $canonicalize = true
để truyền tham số vào hàm là sai lệch - PHP không hỗ trợ các đối số được đặt tên. Điều này thực sự đang làm là thiết lập ba biến đó, sau đó ngay lập tức chuyển các giá trị của chúng cho hàm. Điều này sẽ gây ra vấn đề nếu ba biến đó đã được xác định trong phạm vi cục bộ vì chúng sẽ bị ghi đè.
$this->assertEquals($array1, $array2, "\$canonicalize = true", 0.0, 10, true);
. Tôi có thể sử dụng 4 dòng thay vì 1, nhưng tôi đã không làm điều đó.
$canonicalize
sẽ bị xóa: github.com/sebastianbergmann/phastait/issues/3342 và assertEqualsCanonicalizing()
sẽ thay thế nó.
Vấn đề của tôi là tôi đã có 2 mảng (các khóa mảng không liên quan đến tôi, chỉ là các giá trị).
Ví dụ tôi muốn kiểm tra nếu
$expected = array("0" => "green", "2" => "red", "5" => "blue", "9" => "pink");
có cùng nội dung (thứ tự không liên quan đến tôi) như
$actual = array("0" => "pink", "1" => "green", "3" => "yellow", "red", "blue");
Vì vậy, tôi đã sử dụng mảng_diff .
Kết quả cuối cùng là (nếu các mảng bằng nhau, sự khác biệt sẽ dẫn đến một mảng trống). Xin lưu ý rằng sự khác biệt được tính theo cả hai cách (Cảm ơn @beret, @GordonM)
$this->assertEmpty(array_merge(array_diff($expected, $actual), array_diff($actual, $expected)));
Đối với thông báo lỗi chi tiết hơn (trong khi gỡ lỗi), bạn cũng có thể kiểm tra như thế này (cảm ơn @ DenilsonSá):
$this->assertSame(array_diff($expected, $actual), array_diff($actual, $expected));
Phiên bản cũ có lỗi bên trong:
$ this-> assertEmpty (mảng_diff ($ mảng2, $ mảng1));
$array1
có nhiều giá trị hơn $array2
, thì nó trả về mảng trống mặc dù giá trị mảng không bằng nhau. Bạn cũng nên kiểm tra, kích thước mảng là như nhau, để chắc chắn.
$a1 = [1,2,3,4,5]; $a2 = [1,3,5]; var_dump (array_diff ($a1, $a2)); var_dump (array_diff ($a2, $a1))
assertEmpty
sẽ không in mảng nếu nó không trống, điều này gây bất tiện trong khi kiểm tra gỡ lỗi. Tôi khuyên bạn nên sử dụng : $this->assertSame(array_diff($expected, $actual), array_diff($actual, $expected), $message);
, vì điều này sẽ in thông báo lỗi hữu ích nhất với tối thiểu mã bổ sung. Điều này hoạt động vì A \ B = B \ A A \ B và B \ A trống ⇔ A = B
Array to string conversion
thông báo khi bạn cố gắng truyền một mảng thành một chuỗi. Một cách để khắc phục điều này là bằng cách sử dụngimplode
Một khả năng khác:
$arr = array(23, 42, 108);
$exp = array(42, 23, 108);
sort($arr);
sort($exp);
$this->assertEquals(json_encode($exp), json_encode($arr));
assertEquals
thứ tự không quan trọng.
$this->assertSame($exp, $arr);
phép so sánh tương tự vì $this->assertEquals(json_encode($exp), json_encode($arr));
chỉ khác là chúng ta không phải sử dụng json_encode
Phương pháp trợ giúp đơn giản
protected function assertEqualsArrays($expected, $actual, $message) {
$this->assertTrue(count($expected) == count(array_intersect($expected, $actual)), $message);
}
Hoặc nếu bạn cần thêm thông tin gỡ lỗi khi mảng không bằng nhau
protected function assertEqualsArrays($expected, $actual, $message) {
sort($expected);
sort($actual);
$this->assertEquals($expected, $actual, $message);
}
Nếu mảng có thể sắp xếp, tôi sẽ sắp xếp cả hai trước khi kiểm tra đẳng thức. Nếu không, tôi sẽ chuyển đổi chúng thành các bộ sắp xếp và so sánh chúng.
Sử dụng mảng_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)));
Hoặc với 2 khẳng định (dễ đọc hơn):
// 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)));
Mặc dù bạn không quan tâm đến đơn hàng, nhưng có thể dễ dàng hơn khi tính đến điều đó:
Thử:
asort($foo);
asort($bar);
$this->assertEquals($foo, $bar);
Chúng tôi sử dụng phương pháp trình bao bọc sau trong các Thử nghiệm của mình:
/**
* 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);
}
Nếu các khóa là như nhau nhưng không theo thứ tự này nên giải quyết nó.
Bạn chỉ cần lấy các khóa theo cùng một thứ tự và so sánh kết quả.
/**
* 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);
}
Các giải pháp đã cho không làm được việc cho tôi vì tôi muốn có thể xử lý mảng đa chiều và có một thông điệp rõ ràng về sự khác biệt giữa hai mảng.
Đây là chức năng của tôi
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)."`.");
}
}
}
}
Sau đó sử dụng nó
$this->assertArrayEquals($array1, $array2, array("/"));
Tôi đã viết một số mã đơn giản để đầu tiên nhận được tất cả các khóa từ một mảng đa chiều:
/**
* 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;
}
Sau đó, để kiểm tra rằng chúng có cấu trúc giống nhau bất kể thứ tự các khóa:
$expectedKeys = $this->getAllArrayKeys($expectedData);
$actualKeys = $this->getAllArrayKeys($actualData);
$this->assertEmpty(array_diff($expectedKeys, $actualKeys));
HTH
Nếu các giá trị chỉ là int hoặc chuỗi và không có nhiều cấp độ ....
Tại sao không chỉ sắp xếp các mảng, chuyển đổi chúng thành chuỗi ...
$mapping = implode(',', array_sort($myArray));
$list = implode(',', array_sort($myExpectedArray));
... và sau đó so sánh chuỗi:
$this->assertEquals($myExpectedArray, $myArray);
Nếu bạn chỉ muốn kiểm tra các giá trị của mảng, bạn có thể làm:
$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")));
Một lựa chọn khác, như thể bạn chưa có đủ, là kết hợp kết assertArraySubset
hợp với assertCount
để đưa ra khẳng định của bạn. Vì vậy, mã của bạn sẽ trông giống như.
self::assertCount(EXPECTED_NUM_ELEMENT, $array);
self::assertArraySubset(SUBSET, $array);
Bằng cách này, bạn độc lập trật tự nhưng vẫn khẳng định rằng tất cả các yếu tố của bạn đều có mặt.
assertArraySubset
thứ tự các chỉ số quan trọng vì vậy nó sẽ không hoạt động. tức là self :: assertArraySubset (['a'], ['b', 'a']) sẽ sai, vì [0 => 'a']
không ở bên trong[0 => 'b', 1 => 'a']
assertEquals
đã xử lý nếu các khóa không theo cùng một thứ tự. Tôi chỉ thử nó.