배열의 요소 순서가 중요하지 않거나 변경 될 수있는 경우 두 배열의 객체가 동일하다고 주장하는 좋은 방법은 무엇입니까?
배열의 요소 순서가 중요하지 않거나 변경 될 수있는 경우 두 배열의 객체가 동일하다고 주장하는 좋은 방법은 무엇입니까?
답변:
가장 깨끗한 방법은 새로운 어설 션 방법으로 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 of 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);
입니다. 1 대신 4 줄을 사용할 수는 있지만 그렇게하지 않았습니다.
$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가 비어
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));
간단한 도우미 방법
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)));
또는 두 가지 주장 (읽기 쉬움) :
// 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));
HTH
배열의 값만 테스트하려면 다음을 수행하십시오.
$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']
내부에 없기 때문에 false입니다.[0 => 'b', 1 => 'a']
assertEquals
키가 동일한 순서가 아닌 경우 이미 처리합니다. 방금 테스트했습니다.