Ryan Chandler

Published in PHP

Declaring Expectations Against Array Items in Pest

Pest has an incredible Expectations API that you can use when writing your tests.

It provides a fluent builder pattern for declaring expectations and making assertions against your data.

Here's a very simple example:

$names = [
    'Ryan',
    'John',
    'Jane',
];

expect($names)
    ->toBeArray()
    ->toHaveCount(3)
    ->toMatchArray([
        'Ryan',
        'John',
        'Jane',
    ]);

Each of the method calls makes an assertion on the data provided to expect().

When you're working with objects, you can also use higher-order expectations to make assertions against properties and method return values.

One tricky problem that I have faced today is that you can't use the HigherOrderExpectation object as an array.

This prevents you from doing something like this:

$object = new class {
    public function getNames()
    {
        return [
            'Ryan',
            'John',
            'Jane',
        ];
    }
};

expect($object)
    ->getNames()[0]
    ->toBe('Ryan');

When you call getNames(), the HigherOrderExpectation class will proxy that method call to the original object that was passed in and return a new instance of HigherOrderExpectation with the return value of the method inside.

It does this because the method doesn't explicitly exist on the HigherOrderExpecation class or the Expectation class.

It does the same for properties too. The retrieve() method that is used to proxy the property access actually checks whether the base value is an array or not.

If the value is an array, it will take the property name and try to find a value with that key.

Unfortunately PHP doesn't let you use numeric values when accessing properties, so something like $object->0 fails.

What you can do instead is use PHP's {} interpolation syntax to create dynamically access a property. Pest will then attempt to find an array with that key and return an instance of HigherOrderExpectation with that value.

This means we can test individual array items with the Expectation API.

expect($object)
    ->getNames()->{0}
    ->toBe('Ryan');

This would return the value as index 0 so that we can perform assertions.

If you had non-numeric array keys, you can use the normal property access syntax as Pest will proxy those to the array.

$ages = [
    'Ryan' => 100,
];

expect($ages)
    ->Ryan->toBe(100);

I do have a work in progress branch that adds support for the [] syntax as it definitely feels more familiar, but in some cases the syntax is a bit strange, e.g. expect($value)[0]->toBe('Ryan')[1]->toBe('John')