phpphpunitassertion

PHPUnit assertEquals strict type check


My objective is to ensure that an object graph has expected values and types. I want to ensure every value is of the expected type.

To this end, assertEquals() is unfortunately not useful:

$this->assertEquals(
    [ 'prop' => '0' ],
    [ 'prop' => 0 ]
);
// -> no failures

In this case, assertSame() works nicely:

$this->assertSame(
    [ 'prop' => '0' ],
    [ 'prop' => 0 ]
);
// Failed asserting that Array &0 (
//     'prop' => 0
// ) is identical to Array &0 (
//     'prop' => '0'
// ).

The problem with assertSame() is that it also checks reference for objects:

$this->assertSame(
    (object) [ 'prop' => 0 ],
    (object) [ 'prop' => 0 ]
);
// Failed asserting that two variables reference the same object.

What options do I have?


On a separate note, I'm not sure why this was designed in this way - to me it feels that assertSame() does two things at once (I've have at most verified object class, not references).


Solution

  • So far, I came up with the following option:

    /**
     * @param mixed $expected
     * @param mixed $actual
     * @param string $message
     */
    public function assertObjectGraph($expected, $actual, $message = '')
    {
        $expected = $this->convertObjectsToHashes($expected);
        $actual = $this->convertObjectsToHashes($actual);
    
        $this->assertSame($expected, $actual, $message);
    }
    
    /**
     * @param mixed $value
     * @return mixed
     */
    private function convertObjectsToHashes($value)
    {
        if (is_object($value)) {
            $value = ['__CLASS__' => get_class($value)] + get_object_vars($value);
        }
    
        if (is_array($value)) {
            $value = array_map([$this, __FUNCTION__], $value);
        }
    
        return $value;
    }
    

    Examples:

    $this->assertObjectGraph(
        (object) [ 'prop' => 0 ],
        (object) [ 'prop' => 0 ]
    );
    // -> ok
    
    $this->assertObjectGraph(
        (object) [ 'prop' => 0 ],
        (object) [ 'prop' => '0' ],
    );
    // Failed asserting that Array &0 (
    //     '__CLASS__' => 'stdClass'
    //     'prop' => '0'
    // ) is identical to Array &0 (
    //     '__CLASS__' => 'stdClass'
    //     'prop' => 0
    // ).
    
    class Test{public $prop = 0;}
    $this->assertObjectGraph(
        (object) [ 'prop' => 0 ],
        new Test()
    );
    // Failed asserting that Array &0 (
    //     '__CLASS__' => 'Test'
    //     'prop' => 0
    // ) is identical to Array &0 (
    //     '__CLASS__' => 'stdClass'
    //     'prop' => 0
    // ).