PHPUnit: मुखर दो सरणियाँ समान हैं, लेकिन महत्वपूर्ण नहीं तत्वों का क्रम


132

वस्तुओं के दो सरणियों के बराबर होने का दावा करने का एक अच्छा तरीका क्या है, जब सरणी में तत्वों का क्रम महत्वहीन है, या यहां तक ​​कि परिवर्तन के अधीन है?


क्या आप सरणी में वस्तुओं के बारे में परवाह करते हैं कि वे बराबर हैं या दोनों सरणियों में ऑब्जेक्ट y की x राशि है?
एडोरियन

@edorian दोनों सबसे दिलचस्प होंगे। मेरे मामले में हालांकि प्रत्येक ऐरे में केवल एक ऑब्जेक्ट y है।
koen

कृपया बराबर परिभाषित करें । छँटाई वस्तु वस्तु हैश की तुलना करने के लिए आपको क्या चाहिए? तुम्हें शायद वैसे भी वस्तुओं को क्रमबद्ध करना होगा ।
टेकेशिन

@ तकशिन समान == में। मेरे मामले में वे मूल्य वस्तुएं हैं इसलिए समता आवश्यक नहीं है। मैं शायद एक कस्टम मुखर विधि बना सकता हूं। मुझे इसमें क्या चाहिए, प्रत्येक सरणी में तत्वों की संख्या की गणना करें, और दोनों में प्रत्येक तत्व के लिए बराबर (==) मौजूद होना चाहिए।
कोन्या

7
असल में, PHPUnit 3.7.24 पर, इस $-> दावे का दावा है कि सरणी में समान कुंजी और मान शामिल हैं, किस क्रम में उपेक्षा कर रहे हैं।
डेरेकसन

जवाबों:


38

ऐसा करने का सबसे साफ तरीका यह होगा कि एक नए दावे के साथ फ़ापुनिट का विस्तार किया जाए। लेकिन अब के लिए एक सरल तरीके के लिए यहां एक विचार है। अयोग्य कोड, कृपया सत्यापित करें:

आपके ऐप में कहीं:

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

क्रेग, आप मूल रूप से जो मैंने कोशिश की, उसके करीब हो। वास्तव में array_diff वह है जो मुझे चाहिए था, लेकिन यह वस्तुओं के लिए काम नहीं करता है। : मैं अपने कस्टम अभिकथन बारे में यहां बताए किया phpunit.de/manual/current/en/extending-phpunit.html
कोएन

उचित लिंक अब https और www के बिना है: phpunit.de/manual/current/en/extending-phpunit.html
Xavi Montero

foreach part अनावश्यक है - array_diff_assoc पहले से ही कुंजियों और मूल्यों दोनों की तुलना करता है। संपादित करें: और आपको जांचने की count(array_diff_assoc($b, $a))भी आवश्यकता है ।
15

212

आप 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 के पुराने संस्करणों में आप एक अपरिवर्तित परम $ asonorquals विधि के कैनोनिकलाइज़ का उपयोग कर सकते हैं । यदि आप $ 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


10
बहुत खुबस। यह स्वीकार किया गया उत्तर क्यों नहीं है, @ तोन?
rinogo

7
$delta = 0.0, $maxDepth = 10, $canonicalize = trueफ़ंक्शन में मापदंडों को पारित करने के लिए उपयोग करना भ्रामक है - PHP नाम के तर्कों का समर्थन नहीं करता है। यह वास्तव में क्या कर रहा है उन तीन चर को सेट कर रहा है, फिर तुरंत अपने मूल्यों को फ़ंक्शन में पास कर रहा है। यह समस्याएँ पैदा करेगा यदि उन तीन चर को पहले से ही स्थानीय दायरे में परिभाषित किया गया है क्योंकि वे अधिलेखित हो जाएंगे।
यी जियांग

11
@ यी-जियांग, यह अतिरिक्त तर्कों का अर्थ समझाने का सबसे छोटा तरीका है। यह अधिक स्व-वर्णनात्मक है फिर अधिक स्वच्छ संस्करण $this->assertEquals($array1, $array2, "\$canonicalize = true", 0.0, 10, true);:। मैं 1 के बजाय 4 लाइनों का उपयोग कर सकता था, लेकिन मैंने ऐसा नहीं किया।
प्रयाजनीकोव

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

इसलिए मैंने 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));

अंदर कीड़े के साथ पुराना संस्करण:

$ यह-> assertEmpty (array_diff ($ array2, $ array1));


इस दृष्टिकोण का मुद्दा यह है कि यदि $array1इससे अधिक मान हैं $array2, तो यह खाली सरणी देता है , भले ही सरणी मान समान न हो। आपको यह भी परखना चाहिए, कि सरणी का आकार समान है, सुनिश्चित करने के लिए।
पेट्रोटेक

3
आपको array_diff या array_diff_assoc दोनों तरह से करना चाहिए। यदि एक सरणी दूसरे का सुपरसेट है तो एक दिशा में array_diff रिक्त होगा, लेकिन दूसरे में गैर-रिक्त है। $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 A = B
Denilson Sá Maia

ध्यान दें कि array_diff तुलना के लिए प्रत्येक मान को स्ट्रिंग में कनवर्ट करता है।
कॉन्सटेंटिन पेलेपेलिन

@Checat में जोड़ने के लिए: Array to string conversionजब आप किसी स्ट्रिंग को एक सरणी देने का प्रयास करेंगे तो आपको एक संदेश मिलेगा । इसके आसपास जाने का एक तरीका हैimplode
ub3rst4r

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
Westy92

1
यह बिना छँटाई के भी काम करता है। assertEqualsआदेश के लिए कोई फर्क नहीं पड़ता।
21:05

1
वास्तव में, हम यह भी उपयोग कर सकते हैं $this->assertSame($exp, $arr); जो समान तुलना करता है क्योंकि $this->assertEquals(json_encode($exp), json_encode($arr)); केवल अंतर यह है कि हमें json_encode का उपयोग करने की आवश्यकता नहीं है
अधिकतम

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

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

वह स्मार्ट है :)
क्रिश्चियन

ठीक वही जो मेरे द्वारा खोजा जा रहा था। सरल।
अब्दुल मय

6

भले ही आप आदेश के बारे में परवाह नहीं करते हैं, लेकिन इसे ध्यान में रखना आसान हो सकता है:

प्रयत्न:

asort($foo);
asort($bar);
$this->assertEquals($foo, $bar);

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

-3

एक अन्य विकल्प, जैसे कि आपके पास पहले से ही पर्याप्त नहीं था, assertArraySubsetसंयुक्त रूप से assertCountअपना दावा करने के लिए संयुक्त है । तो, आपका कोड कुछ ऐसा दिखाई देगा।

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

इस तरह से आप स्वतंत्र हैं लेकिन फिर भी यह दावा करते हैं कि आपके सभी तत्व मौजूद हैं।


में assertArraySubsetइंडेक्सों का आदेश तो कोई फर्क यह काम नहीं करेगा। अर्थात स्व :: assertArraySubset (['a'], [, b ',' a ’) झूठा होगा, क्योंकि [0 => 'a']अंदर नहीं है[0 => 'b', 1 => 'a']
रॉबर्ट टी।

क्षमा करें, लेकिन मुझे रॉबर्ट से बात करनी है। पहले तो मैंने सोचा कि स्ट्रिंग कुंजियों के साथ सरणियों की तुलना करने के लिए यह एक अच्छा समाधान होगा, लेकिन assertEqualsपहले से ही संभालता है कि यदि चाबियाँ समान क्रम में नहीं हैं। मैंने अभी इसका परीक्षण किया।
कोडोस जॉनसन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.