Phpunit: assertArraySubset() should provide the diff when assertion fails

Created on 7 Jul 2016  ·  4Comments  ·  Source: sebastianbergmann/phpunit

Greetings, noble developers of phpunit
The talk will go about PHPUnit_Framework_Assert::assertArraySubset() introduced in https://github.com/sebastianbergmann/phpunit/pull/1377

Thank you very much for providing this useful assertion function.
It is very nice, but lacks one very important thing - info about _what exactly mismatched_

Consider such code:

$expected = [
    'a' => 'item a',
    'b' => 'item b',
    'c' => [
        'd' => 'item d',
    ],
];

$this->assertArraySubset($expected, [
    'a' => 'item a',
    'b' => 'item k', // wrong value
    'c' => [
        // 'd' not present
        'g' => 'item g',
    ],
]);

With current implementation it would generate such error:

1) RetrievalCommandManagerTest::checkServicesImportProcess with data set #0 ...
Failed asserting that an array has the subset Array &0 (
    'a' => 'item a'
    'b' => 'item b'
    'c' => Array &1 (
        'd' => 'item d'
    )
).

You see what's fishy here? It tells you that subset is incorrect, but it does not tell, where _exactly_ it is incorrect.

How proper error should look like:

1) RetrievalCommandManagerTest::checkServicesImportProcess with data set #0 ...
Failed asserting that an array has the subset.
--- Expected
+++ Actual
@@ @@
Array &0 (
       'a' => 'item a'
---    'b' => 'item b'
+++    'b' => 'item k'
       'c' => Array &1 (
---        'd' => 'item d'
       )
).

I can implement the failure message with diff and do a pull request. Do you like the idea?

typbug

Most helpful comment

@jmauerhan, thanks for reply

I believe it shouldn't.
If i correctly see the idea of assertArraySubset(), it is "make sure that keys I specify in this subset match the data and nevermind rest keys".

The way our team (and I believe rest users too) use assertArraySubset() is to check some most important parts of output.

So, absolutely definitely keys not specified in the subset ("g" in this case) should not be present in the diff.

All 4 comments

How I basically override the function it in my top-level code:

public static function assertArraySubset(array $expectation, array $reality, 
                                         $strict = false, $message = '')
{
//    parent::assertEquals($expectation, $reality, $strict, $message);
    foreach ($expectation as $key => $value) {
        self::assertArrayHasKey($key, $reality, $message);
        if (is_array($value)) {
            self::assertArraySubset($value, $reality[$key], $strict, $message.'['.$key.']');
        } else {
            self::assertEquals($value, $reality[$key], $message.'['.$key.']');
        }
    }
}

Should your diff also include the 'g' key which is present but not expected?

@jmauerhan, thanks for reply

I believe it shouldn't.
If i correctly see the idea of assertArraySubset(), it is "make sure that keys I specify in this subset match the data and nevermind rest keys".

The way our team (and I believe rest users too) use assertArraySubset() is to check some most important parts of output.

So, absolutely definitely keys not specified in the subset ("g" in this case) should not be present in the diff.

This would be a really helpful feature. Is there any progress on this?

Was this page helpful?
0 / 5 - 0 ratings