0

Some of my PHPUnit tests call the Facebook "php-business-sdk" so that I can be confident that Facebook's API is continuing to operate as I expect. E.g. getInsights() within FacebookAds\Object\Ad.

And those PHPUnit tests have been using assertEqualsCanonicalizing.

However, the tests are still brittle; Facebook's API often changes not just the order of the top-level array in the result (an array of associative arrays) but also the order of the keys inside the associative arrays.

So what I really need is a version of assertEqualsCanonicalizing that is recursive and agnostic to the sorting of the keys of the associative arrays within that top-level array too.

I'd rather not code my own if such a function already exists (maybe in PHP, PHPUnit, Laravel, or elsewhere). Does it?

P.S. Here is a simplified example of a result:

[
  {
    "Spend": "$3,009",
    "Campaign ID": 3335626793661,
    "Reach": 37640,
    "Unique Inline Link Clicks": 2368
  },
  {
    "Spend": "$1,030",
    "Campaign ID": 3335626793662,
    "Reach": 1620,
    "Unique Inline Link Clicks": 231
  }
]

(Imagine next time the API returns the same data but with "Reach" being written before "Spend", and the order of the objects can change too.)

P.S. This is not a duplicate of the linked question because I'm specifically asking how to be agnostic of the sorting order of the inner array keys.

Ryan
  • 22,332
  • 31
  • 176
  • 357
  • Possible duplicate of [PHPUnit: assert two arrays are equal, but order of elements not important](https://stackoverflow.com/questions/3838288/phpunit-assert-two-arrays-are-equal-but-order-of-elements-not-important) – Yassine CHABLI Jul 12 '19 at 14:34
  • @MohammedYassineCHABLI I updated my question to show how it's not a duplicate, and I also provided an answer. How can I now remove the warning about it being a possible duplicate? Or can you change your vote? – Ryan Jul 12 '19 at 22:06

2 Answers2

1

There isn't a native method for in_array which works recursively.

But many people have solved this issue with a helper like this one:

    private function in_array_recursive($needle, $haystack, $strict = true)
    {
        foreach ($haystack as $value) {
            if (($strict ? $value === $needle : $value == $needle) || (is_array($value) && $this->in_array_recursive($needle, $value, $strict))) {
                return true;
            }
        }
        return false;
    }
stokoe0990
  • 443
  • 3
  • 10
0

Until someone shows an existing native function or something better than this, this is what seems to work for my purposes:

/**
 * @see https://stackoverflow.com/q/57008999/470749
 *
 * @param array $expected
 * @param array $actual
 */
public function assertEqualsCanonicalizingInner($expected, $actual) {
    try {
        $this->assertEqualsCanonicalizing(AT::sortInnerArrays($expected), AT::sortInnerArrays($actual));
    } catch (\Exception $e) {
        $expectedSortedJson = json_encode(AT::sortInnerArrays($expected));
        $actualSortedJson = json_encode(AT::sortInnerArrays($actual));
        $this->assertTrue(false, __FUNCTION__ . ' failed: ' . PHP_EOL . $expectedSortedJson . PHP_EOL . ' vs ' . PHP_EOL . $actualSortedJson);
    }
}

/**
 * @see https://stackoverflow.com/q/57008999/470749
 * 
 * @param array $arr
 * @return array
 */
public static function sortInnerArrays($arr) {
    $resultingOuterArr = [];
    foreach ($arr as $k => $v) {
        if (is_array($v)) {
            foreach ($v as $kInner => $vInner) {
                if (is_array($vInner)) {
                    $v[$kInner] = self::sortInnerArrays($vInner);
                }
            }
            ksort($v);
        }
        $resultingOuterArr[$k] = $v;
    }
    sort($resultingOuterArr);
    return $resultingOuterArr;
}
Ryan
  • 22,332
  • 31
  • 176
  • 357