'How to assert parameter attribute sub-structures in PHPUnit mock call expectations

I am testing an parameter sent to a mocked event handler. The parameter is an object of "Event" sub-type, which itself has some data nested inside it. I want to test the Event and its substructure matches the fixture data I've injected into the code through various mocks.

I can test the "top level" of the event easily enough: the classname, and simple attributes like an event name string. I can also test that an attribute contains the same object, which I believe implicitly tests all the substructure of the object.

The problem I'm having is some of the sub-structure in a more complex example is causing the test to fail but it's irrelevant, so I want to cherry-pick specific properties of the sub-structure, and not just identity-compare the entire object.

I feel like I'm missing something in the attribute assertions: how to access the parameter that the "with" refers to - as variable. Then I could pass it into some of the assert methods like attributeEqualTo which require the item under test to be passed in. Perhaps these just cannot be used in the fluent case I'm using?

  • I'd like to check the event.data is a certain class.
  • I'd like to check the event.data.thing1 == X
  • I'd like to check the event.data.thing2 == Y and so on.

Simplified code:

class MyEventData{
    public $thing1;
    public $thing2;
}

class MyEvent{
    public $data;
}

// An event gets fired containing this in the tests
$eventData = new MyEventData(1,2);

$this->eventMock->expects($this->exactly(3))
    ->method('fire')
    ->with(
        $this->logicalAnd(
        // THIS WORKS OK
            $this->isInstanceOf('\MyApp\MyEvents\SomeEvent'), 
            // THIS WORKS OK
            $this->attributeEqualTo ('name', SomeEvent::EVENT_NAME),    
            // THIS WORKS in simplified cases only
            $this->attributeEqualTo ('data', $eventData),

            // HOW DO I GET THE "WITH" PARAMETER CONTEXT "INTO" THE THIRD PARAMETER? 
            $this->assertAttributeInstanceOf('\MyApp\MyEvents\MyEventData', 'data', -classOrObject- ),  

            // Then how can I test with attribute data.thing1 == 1 and data.thing2 = 2

        )
    );


Solution 1:[1]

I've got it to work using the callback constraint, but it feels like I've now stepped off the path and lost the power of PHPUnit - I can't seem to use the assertion helpers here anymore.

e.g. If the accumulated tests return false, I don't get any details in the output log beyond "Expectation failed for ... and is accepted by specified callback".

    $this->callback(function($subject){
        $b = true;
        // I tried using this constraint but can't access (autoload) this class? So is it not supposed to be used directly?
        //$c = new PHPUnit_Framework_Constraint_IsInstanceOf('\MyApp\MyEvents\MyEventData');
        // return $c->matches(subject);

        // this is the right assert, but it doesn't return the result, so I cannot use it in a callback constraint.
        \PHPUnit_Framework_Assert::assertAttributeInstanceOf('\MyApp\MyEvents\MyEventData', 'data', $subject);

        // This works but seems very "Manual"
        $b = $b && get_class($subject->data) == '\MyApp\MyEvents\MyEventData';
        $b = $b && $subject->data->thing1 == 1;
        $b = $b && $subject->data->thing2 == 1;
        return $b;
    })

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 scipilot