পিএইচপিউনিট: জোড় করে দুটি অ্যারে সমান, তবে উপাদানগুলির ক্রম গুরুত্বপূর্ণ নয়


132

অ্যারেতে থাকা উপাদানগুলির ক্রম গুরুত্বহীন বা এমনকি পরিবর্তনের সাপেক্ষে যখন দুটি অ্যারের অবজেক্ট সমান হয় তা জোর দিয়ে দেওয়ার একটি ভাল উপায় কী?


আপনি অ্যারে মৌমাছি সমীকরণের অবজেক্টগুলি সম্পর্কে সমান বা উভয় অ্যারেতে x পরিমাণে অবজেক্ট y এর পরিমাণ সম্পর্কে চিন্তা করেন?
এডোরিয়ান

@ এডোরিয়ান উভয়ই সবচেয়ে আকর্ষণীয় হবে। আমার ক্ষেত্রে যদিও প্রতিটি অ্যারেতে কেবল একটি অবজেক্ট y রয়েছে।
Koen

সমান সংজ্ঞা দিন । সাজানো অবজেক্টের হ্যাশগুলির তুলনা করা কি আপনার প্রয়োজন? আপনাকে সম্ভবত যেকোন উপায়ে বাছাই করতে হবে।
তাকেশিন

@ টাকেশিন সমান == তেমন। আমার ক্ষেত্রে তারা মান বস্তু তাই সমতা প্রয়োজন হয় না। আমি সম্ভবত একটি কাস্টম সংস্থান পদ্ধতি তৈরি করতে পারে। এতে আমার যা দরকার তা হ'ল প্রতিটি অ্যারেতে উপাদানগুলির সংখ্যা গণনা করা এবং উভয় সমতুল্য (==) উভয় উপাদানের জন্য অবশ্যই বিদ্যমান।
Koen

7
প্রকৃতপক্ষে, পিএইচপিউইনিত ৩..2.২৪ এ $ এটি-> অ্যাসেটএকোয়ালস দৃser়ভাবে জানিয়েছে যে অ্যারেটিতে একই কী এবং মান রয়েছে, কী ক্রমে তা উপেক্ষা করে।
ডেরিকসন

উত্তর:


38

এটি করার সর্বোত্তম উপায় হ'ল কোনও নতুন আসক্তি পদ্ধতি সহ 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));

ক্রেগ, আমি যা চেষ্টা করেছি তার কাছাকাছি আপনি। আসলে অ্যারে_ডিফটি আমার যা প্রয়োজন তা হ'ল তবে এটি বস্তুর জন্য কাজ করছে বলে মনে হয় না। : আমি আমার কাস্টম কথন লিখতে এখানে ব্যাখ্যা করেনি phpunit.de/manual/current/en/extending-phpunit.html
Koen

সঠিক লিঙ্কটি এখন https এর সাথে এবং www ছাড়াই: phpunit.de/manual/current/en/extending-phpunit.html
জাভি মন্টেরো

foreach অংশ অপ্রয়োজনীয় - অ্যারে_ডিফ_সোক ইতিমধ্যে কী এবং মান উভয়ের তুলনা করে। সম্পাদনা করুন: এবং আপনারও পরীক্ষা করা দরকার count(array_diff_assoc($b, $a))
জনস্মিত

212

আপনি assertEqualsCanonicalization পদ্ধতিটি ব্যবহার করতে পারেন যা পিএইচপিউনিট 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;
    }
}

পিএইচপিউইন্টের পুরানো সংস্করণগুলিতে আপনি একটি নির্বিঘ্নিত পরম ব্যবহার করতে পারেন $ assertEquals পদ্ধতির canonicalize । আপনি যদি $ canonicalize = সত্যটি পাস করেন তবে আপনি একই প্রভাব পাবেন:

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


10
ফ্যান্টাস্টিক। এটি কেন অনুমোদিত উত্তর নয়, @ কোয়েন?
রিনোগো

7
ফাংশনটিতে $delta = 0.0, $maxDepth = 10, $canonicalize = trueপরামিতিগুলি ব্যবহার করা বিভ্রান্তিমূলক - পিএইচপি নামযুক্ত যুক্তি সমর্থন করে না। এটি আসলে যা করছে তা হ'ল তিনটি ভেরিয়েবল সেট করা, তারপরে তত্ক্ষণাত তাদের মানগুলি ফাংশনে প্রেরণ করা। যদি এই তিনটি ভেরিয়েবলগুলি স্থানীয় স্কোপে ইতিমধ্যে সংজ্ঞায়িত করা হয় তবে সেগুলি ওভাররাইট হবে।
ই জিয়াং

11
@ ই-জিয়াং, অতিরিক্ত যুক্তিগুলির অর্থ ব্যাখ্যা করার জন্য এটি সবচেয়ে সংক্ষিপ্ততম উপায়। এটা আরো স্ব-বর্ণনামূলক তারপর আরো পরিষ্কার বৈকল্পিক আছে: $this->assertEquals($array1, $array2, "\$canonicalize = true", 0.0, 10, true);। আমি 1 এর পরিবর্তে 4 টি লাইন ব্যবহার করতে পারি, তবে আমি এটি করিনি।
pryazhnikov

8
আপনি উল্লেখ করতে পারেন না যে এই সমাধানটি কীগুলি বাতিল করে দেবে।
ওডাল্রিক

8
নোটটি $canonicalizeমুছে ফেলা হবে: github.com/sebastianbergmann/phpunit/issues/3342 এবং assertEqualsCanonicalizing()এটি প্রতিস্থাপন করবে।
Koen

35

আমার সমস্যাটি ছিল আমার 2 টি অ্যারে ছিল (অ্যারে কীগুলি আমার জন্য প্রাসঙ্গিক নয়, কেবল মানগুলি)।

উদাহরণস্বরূপ আমি যদি পরীক্ষা করতে চেয়েছিলাম

$expected = array("0" => "green", "2" => "red", "5" => "blue", "9" => "pink");

একই কন্টেন্ট ছিল (আমার জন্য প্রাসঙ্গিক অর্ডার নয়)

$actual = array("0" => "pink", "1" => "green", "3" => "yellow", "red", "blue");

সুতরাং আমি অ্যারে_ডিফ ব্যবহার করেছি

চূড়ান্ত ফলাফলটি ছিল (যদি অ্যারেগুলি সমান হয় তবে পার্থক্যটি খালি অ্যারে তৈরি করবে)। দয়া করে মনে রাখবেন যে পার্থক্যটি উভয় উপায়ে গণনা করা হয়েছে (ধন্যবাদ @ বারেট, @ গর্ডনএম)

$this->assertEmpty(array_merge(array_diff($expected, $actual), array_diff($actual, $expected)));

আরও বিশদ ত্রুটির বার্তার জন্য (ডিবাগ করার সময়) আপনিও এটি পরীক্ষা করতে পারেন (ধন্যবাদ @ ডেনিলসনá):

$this->assertSame(array_diff($expected, $actual), array_diff($actual, $expected));

ভিতরে বাগ সহ পুরানো সংস্করণ:

$ এটি-> assertEmpty (অ্যারে_ডিফ ($ অ্যারে 2, $ অ্যারে 1));


এই পদ্ধতির ইস্যুটি হ'ল যদি $array1এর চেয়ে বেশি মান থাকে $array2তবে অ্যারের মান সমান না হলেও এটি খালি অ্যারে প্রদান করে। আপনারও পরীক্ষা করা উচিত, নিশ্চিত হওয়ার জন্য যে অ্যারের আকার একই is
পেট্রকোটেক

3
আপনার অ্যারে_ডিফ বা অ্যারে_ডিফ_্যাসোক উভয় উপায়ে করা উচিত। যদি একটি অ্যারে অন্যের সুপারস্টেট হয় তবে একদিকে অ্যারে_ডিফ ফাঁকা হবে, তবে অন্যদিকে শূন্য থাকবে না। $a1 = [1,2,3,4,5]; $a2 = [1,3,5]; var_dump (array_diff ($a1, $a2)); var_dump (array_diff ($a2, $a1))
গর্ডনএম

2
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
উপায়টি

20

অন্য একটি সম্ভাবনা:

  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));

যদি হয় অ্যারেতে অবজেক্ট থাকে তবে json_encode কেবল সর্বজনীন বৈশিষ্ট্যগুলিকে এনকড করে। এটি এখনও কাজ করবে, তবে কেবলমাত্র যদি সমতা নির্ধারণ করে এমন সমস্ত সম্পত্তি জনসাধারণ হয়। ব্যক্তিগত সম্পত্তিগুলির json_encoding নিয়ন্ত্রণ করতে নিম্নলিখিত ইন্টারফেসটি দেখুন। php.net/manual/en/class.jsonserializable.php
ওয়েস্টি 9 2

1
এটি এমনকি বাছাই ছাড়াই কাজ করে। জন্য assertEqualsঅর্ডার কোন ব্যাপার না।
উইল

1
প্রকৃতপক্ষে, আমরা ব্যবহার করতে পারেন $this->assertSame($exp, $arr); যা একই তুলনা করে যেমন $this->assertEquals(json_encode($exp), json_encode($arr)); শুধু পার্থক্য আমরা json_encode ব্যবহার করতে হবে না
Maxwells

15

সাধারণ সহায়ক পদ্ধতি

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);
}

8

অ্যারেটি বাছাইযোগ্য হলে সাম্যতা যাচাই করার আগে আমি তাদের উভয়কে বাছাই করব। যদি তা না হয় তবে আমি তাদের কোনও ধরণের সেটগুলিতে রূপান্তর করব এবং সেগুলির সাথে তুলনা করব।


6

অ্যারে_ডিফ () ব্যবহার করে :

$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

আমরা আমাদের টেস্টগুলিতে নিম্নলিখিত র‍্যাপার পদ্ধতিটি ব্যবহার করি:

/**
 * 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);
}

5

কীগুলি যদি একই হয় তবে ক্রম ছাড়াই এটির সমাধান করা উচিত।

আপনাকে কেবল একই ক্রমে কীগুলি পেতে হবে এবং ফলাফলগুলি তুলনা করতে হবে।

 /**
 * 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);
}

3

প্রদত্ত সমাধানগুলি আমার পক্ষে কাজটি করেনি কারণ আমি বহু-মাত্রিক অ্যারে হ্যান্ডেল করতে সক্ষম হতে এবং দুটি অ্যারের মধ্যে কী আলাদা তা সম্পর্কে একটি পরিষ্কার বার্তা পেতে চেয়েছিলাম।

এখানে আমার ফাংশন

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("/"));

1

আমি প্রথমে একটি বহুমাত্রিক অ্যারে থেকে সমস্ত কীগুলি পেতে কিছু সাধারণ কোড লিখেছিলাম:

 /**
 * 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


0

মানগুলি যদি কেবল ইনট বা স্ট্রিং হয় এবং একাধিক স্তরের অ্যারে হয় না ....

কেন কেবল অ্যারে বাছাই করা হচ্ছে না, তাদের স্ট্রিংয়ে রূপান্তর করুন ...

    $mapping = implode(',', array_sort($myArray));

    $list = implode(',', array_sort($myExpectedArray));

... এবং তারপরে স্ট্রিংয়ের তুলনা করুন:

    $this->assertEquals($myExpectedArray, $myArray);

-2

আপনি যদি কেবল অ্যারের মানগুলি পরীক্ষা করতে চান তবে আপনি করতে পারেন:

$this->assertEquals(array_values($arrayOne), array_values($arrayTwo));

1
দুর্ভাগ্যক্রমে এটি "কেবলমাত্র মানগুলি" নয় তবে মান এবং মানগুলির ক্রম উভয়ই পরীক্ষা করছে। উদাহরণস্বরূপecho("<pre>"); print_r(array_values(array("size" => "XL", "color" => "gold"))); print_r(array_values(array("color" => "gold", "size" => "XL")));
পকেটস্যান্ড

-3

আরেকটি বিকল্প, যেমন যদি আপনি ইতিমধ্যে যথেষ্ট আছে না, একত্রিত হয় assertArraySubsetসঙ্গে মিলিত assertCountআপনার কথন করা। সুতরাং, আপনার কোডটি দেখতে এমন কিছু দেখাচ্ছে।

self::assertCount(EXPECTED_NUM_ELEMENT, $array); self::assertArraySubset(SUBSET, $array);

এইভাবে আপনি স্বাধীন অর্ডার করছেন তবে এখনও দৃ still়ভাবে বলুন যে আপনার সমস্ত উপাদান উপস্থিত রয়েছে।


ইন assertArraySubsetইনডেক্স ক্রম তাই কোন ব্যাপার এটা কাজ করবে না। অর্থাৎ স্ব :: :: assertArraySubset (['a'], ['বি', 'a']) মিথ্যা হবে, কারণ [0 => 'a']এটি ভিতরে নেই[0 => 'b', 1 => 'a']
রবার্ট টি

দুঃখিত তবে রবার্টের সাথে আমার একমত হতে হবে। প্রথমে আমি ভেবেছিলাম স্ট্রিং কীগুলির সাথে অ্যারের তুলনা করা এটি ভাল সমাধান হবে তবে assertEqualsইতিমধ্যে হ্যান্ডলগুলি হয় যে কীগুলি একই ক্রমে না থাকলে। আমি ঠিক এটি পরীক্ষা করেছি।
কোডোস জনসন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.