PHPUnit: утверждают, что два массива равны, но порядок элементов не важен - PullRequest
118 голосов
/ 01 октября 2010

Какой хороший способ утверждать, что два массива объектов равны, когда порядок элементов в массиве не важен или даже подлежит изменению?

Ответы [ 15 ]

167 голосов
/ 28 января 2015

Вы можете использовать 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 вы можете использовать недокументированный параметр $канонизировать метод 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

34 голосов
/ 24 октября 2012

Моя проблема заключалась в том, что у меня было 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));

33 голосов
/ 02 октября 2010

Самый простой способ сделать это - расширить 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));
20 голосов
/ 15 мая 2013

Еще одна возможность:

  1. Сортировка обоих массивов
  2. Преобразование их в строку
  3. Утверждение, что обе строки равны

$arr = array(23, 42, 108);
$exp = array(42, 23, 108);

sort($arr);
sort($exp);

$this->assertEquals(json_encode($exp), json_encode($arr));
14 голосов
/ 19 сентября 2012

Простой вспомогательный метод

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);
}
7 голосов
/ 01 октября 2010

Если массив можно сортировать, я бы отсортировал их оба перед проверкой равенства.Если нет, я бы преобразовал их в какие-то наборы и сравнил бы их.

6 голосов
/ 24 апреля 2015

Использование 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)));
5 голосов
/ 28 ноября 2014

Если ключи совпадают, но не в порядке, это должно решить эту проблему.

Вам просто нужно получить ключи в том же порядке и сравнить результаты.

 /**
 * 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);
}
5 голосов
/ 01 марта 2013

Мы используем следующий метод обертки в наших тестах:

/**
 * 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);
}
4 голосов
/ 21 декабря 2016

Даже если вы не заботитесь о заказе, может быть проще принять это во внимание:

Попытка:

asort($foo);
asort($bar);
$this->assertEquals($foo, $bar);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...