Dizideki öğelerin sırası önemsiz olduğunda veya hatta değişime açık olduğunda, iki nesne dizisinin eşit olduğunu iddia etmenin iyi bir yolu nedir?
Dizideki öğelerin sırası önemsiz olduğunda veya hatta değişime açık olduğunda, iki nesne dizisinin eşit olduğunu iddia etmenin iyi bir yolu nedir?
Yanıtlar:
Bunu yapmanın en temiz yolu, phpunit'i yeni bir iddia yöntemiyle genişletmek olacaktır. Ama işte şimdilik daha basit bir yol için bir fikir. Test edilmemiş kod, lütfen doğrulayın:
Uygulamanızda bir yerde:
/**
* 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;
}
Testinizde:
$this->assertTrue(arrays_are_similar($foo, $bar));
count(array_diff_assoc($b, $a))
ayrıca kontrol etmeniz gerekir .
PHPUnit 7.5'e eklenen assertEqualsCanonicalizing yöntemini kullanabilirsiniz . Bu yöntemi kullanarak dizileri karşılaştırırsanız, bu diziler PHPUnit dizileri karşılaştırıcısının kendisine göre sıralanır.
Kod örneği:
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'in eski sürümlerinde, assertEquals yönteminin belgelenmemiş $ canonicalize parametresini kullanabilirsiniz . Eğer geçerseniz $ canonicalize = true , aynı etkiyi elde edecek:
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'in en son sürümündeki diziler karşılaştırıcı kaynak kodu: https://github.com/sebastianbergmann/comparator/blob/master/src/ArrayComparator.php#L46
$delta = 0.0, $maxDepth = 10, $canonicalize = true
Parametreleri işleve geçirmek için kullanmak yanıltıcıdır - PHP adlandırılmış argümanları desteklemez. Bunun aslında yaptığı şey, bu üç değişkeni ayarlamak ve ardından değerlerini hemen işleve geçirmektir. Bu üç değişken, üzerine yazılacağı için yerel kapsamda zaten tanımlanmışsa sorunlara neden olacaktır.
$this->assertEquals($array1, $array2, "\$canonicalize = true", 0.0, 10, true);
. 1 yerine 4 satır kullanabilirdim ama bunu yapmadım.
$canonicalize
bunun kaldırılacağını unutmayın : github.com/sebastianbergmann/phpunit/issues/3342 ve assertEqualsCanonicalizing()
onun yerini alacaktır.
Benim sorunum 2 diziye sahip olmamdı (dizi anahtarları benim için önemli değil, sadece değerler).
Örneğin eğer test etmek istedim
$expected = array("0" => "green", "2" => "red", "5" => "blue", "9" => "pink");
aynı içeriğe sahipti (sipariş benim için uygun değil)
$actual = array("0" => "pink", "1" => "green", "3" => "yellow", "red", "blue");
Bu yüzden array_diff kullandım .
Nihai sonuç oldu (diziler eşitse, fark boş bir dizi ile sonuçlanacaktır). Lütfen farkın her iki şekilde hesaplandığını unutmayın (Teşekkürler @beret, @GordonM)
$this->assertEmpty(array_merge(array_diff($expected, $actual), array_diff($actual, $expected)));
Daha ayrıntılı bir hata mesajı için (hata ayıklama sırasında), şu şekilde de test edebilirsiniz (teşekkürler @ DenilsonSá):
$this->assertSame(array_diff($expected, $actual), array_diff($actual, $expected));
İçinde böcek bulunan eski sürüm:
$ this-> assertEmpty (array_diff ($ dizi2, $ dizi1));
$array1
daha fazla değere sahipse , $array2
dizi değerleri eşit olmasa bile boş dizi döndürmesidir. Ayrıca emin olmak için dizi boyutunun aynı olduğunu test etmelisiniz.
$a1 = [1,2,3,4,5]; $a2 = [1,3,5]; var_dump (array_diff ($a1, $a2)); var_dump (array_diff ($a2, $a1))
assertEmpty
diziyi boş değilse yazdırmaz, bu da testlerde hata ayıklama sırasında sakıncalıdır. Kullanmanızı öneririm:, $this->assertSame(array_diff($expected, $actual), array_diff($actual, $expected), $message);
çünkü bu, minimum ekstra kodla en yararlı hata mesajını yazdıracaktır. Bu işe yarar
Array to string conversion
Bir diziyi bir dizeye dönüştürmeye çalıştığınızda bir mesaj alacaksınız . Bunu implode
Bir başka olasılık:
$arr = array(23, 42, 108);
$exp = array(42, 23, 108);
sort($arr);
sort($exp);
$this->assertEquals(json_encode($exp), json_encode($arr));
assertEquals
önemli değil sırayla.
$this->assertSame($exp, $arr);
benzer bir karşılaştırma yapan hangisini $this->assertEquals(json_encode($exp), json_encode($arr));
kullanabiliriz
Basit yardımcı yöntem
protected function assertEqualsArrays($expected, $actual, $message) {
$this->assertTrue(count($expected) == count(array_intersect($expected, $actual)), $message);
}
Veya diziler eşit olmadığında daha fazla hata ayıklama bilgisine ihtiyacınız varsa
protected function assertEqualsArrays($expected, $actual, $message) {
sort($expected);
sort($actual);
$this->assertEquals($expected, $actual, $message);
}
Dizi sıralanabiliyorsa, eşitliği kontrol etmeden önce ikisini de sıralarım. Değilse, onları bir tür setlere dönüştürür ve karşılaştırırdım.
Array_diff () kullanarak :
$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)));
Veya 2 iddia ile (daha kolay okunur):
// 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)));
Siparişi önemsemeseniz bile, bunu hesaba katmak daha kolay olabilir:
Deneyin:
asort($foo);
asort($bar);
$this->assertEquals($foo, $bar);
Testlerimizde aşağıdaki sarmalayıcı yöntemini kullanıyoruz:
/**
* 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);
}
Anahtarlar aynıysa ancak sıra dışıysa bu sorunu çözmelidir.
Anahtarları aynı sırayla almanız ve sonuçları karşılaştırmanız yeterlidir.
/**
* 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);
}
Verilen çözümler benim için işi yapmadı çünkü çok boyutlu diziyi işleyebilmek ve iki dizi arasında neyin farklı olduğuna dair net bir mesaja sahip olmak istedim.
İşte benim fonksiyonum
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)."`.");
}
}
}
}
Sonra kullanmak için
$this->assertArrayEquals($array1, $array2, array("/"));
İlk önce tüm anahtarları çok boyutlu bir diziden almak için bazı basit kodlar yazdım:
/**
* 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;
}
Ardından, anahtarların sırasına bakılmaksızın aynı şekilde yapılandırıldıklarını test etmek için:
$expectedKeys = $this->getAllArrayKeys($expectedData);
$actualKeys = $this->getAllArrayKeys($actualData);
$this->assertEmpty(array_diff($expectedKeys, $actualKeys));
HTH
Değerler yalnızca int veya dizelerden ibaretse ve birden çok düzey dizisi yoksa ...
Neden dizileri sıralamak, dizgeye dönüştürmek değil ...
$mapping = implode(',', array_sort($myArray));
$list = implode(',', array_sort($myExpectedArray));
... ve sonra dizeyi karşılaştırın:
$this->assertEquals($myExpectedArray, $myArray);
Yalnızca dizinin değerlerini test etmek istiyorsanız şunları yapabilirsiniz:
$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")));
Başka bir seçenek, zaten yeterince yoktu sanki birleştirmek için assertArraySubset
kombine assertCount
iddianızı yapmak. Yani kodunuz şuna benzer.
self::assertCount(EXPECTED_NUM_ELEMENT, $array);
self::assertArraySubset(SUBSET, $array);
Bu şekilde düzenden bağımsız olursunuz, ancak yine de tüm öğelerinizin mevcut olduğunu iddia edersiniz.
assertArraySubset
endeksler sırasına o işi olmaz bu yüzden önemli. yani öz :: assertArraySubset ([ 'a'], [ 'b', 'a']), çünkü yanlış olacaktır [0 => 'a']
içinde değil[0 => 'b', 1 => 'a']
assertEquals
anahtarlar aynı sırada değilse bunu zaten hallediyor. Daha yeni test ettim.