Expectations

Overview

In addition to assertions, Pest offers you a set of expectations. These functions let you test your values against certain conditions. This API is inspired by Jest. Expectations also allow you to write your tests like you would a natural sentence:

test('expect true to be true', function () {
  // assertion
  $this->assertTrue(true);

  // expectation
  expect(true)->toBe(true);
});

expect($value)

Start the expectation by passing your value to this function. Then, you can chain your check(s):

expect($value)->// chain your checks here

Available Expectations

toBe()

Asserts that two variables have the same type and value. Used on objects, it asserts that the two variables reference the same object:

expect($color)->toBe('pink');

expect($user)->toBe($user);

toBeEmpty()

Asserts that the value is empty:

expect($lastSeen)->toBeEmpty();

toBeTrue()

Asserts that the value is true:

expect($published)->toBeTrue();

toBeFalse()

Asserts that the value is false:

expect($archived)->toBeFalse();

toBeGreaterThan($expected)

Asserts that the value is greater than the expected one:

expect($age)->toBeGreaterThan(20);

toBeGreaterThanOrEqual($expected)

Asserts that the value is greater than or equal to the expected one:

expect($age)->toBeGreaterThanOrEqual(21);

toBeLessThan($expected)

Asserts that the value is lesser than the expected one:

expect($count)->toBeLessThan(3);

toBeLessThanOrEqual($expected)

Asserts that the value is lesser than or equal to the expected one:

expect($count)->toBeLessThanOrEqual(2);

toContain($needle)

Asserts that the needle is an element of the value:

expect($content)->toContain('Hello World');

toHaveCount(int $count)

Asserts that the $count matches the number of elements of $value:

expect($dozen)->toHaveCount(12);

toHaveProperty(string $name, $value = null)

Asserts that the $value contains the property $name:

expect($user)->toHaveProperty('name');
expect($user)->toHaveProperty('name', 'Nuno');

toMatchArray($array)

Asserts that the $value array matches the given $array subset.

$user = [
    'id'    => 1,
    'name'  => 'Nuno',
    'email' => 'enunomaduro@gmail.com',
];

expect($user)->toMatchArray([
    'email' => 'enunomaduro@gmail.com',
    'name' => 'Nuno'
]);

toMatchObject($object)

Asserts that the $value object matches a subset of the properties of a given $object:

$user = new stdClass();
$user->id = 1;
$user->email = 'enunomaduro@gmail.com';
$user->name = 'Nuno';

expect($user)->toMatchObject([
    'email' => 'enunomaduro@gmail.com',
    'name' => 'Nuno'
]);

toEqual($expected)

Asserts that two variables have the same value.

expect($title)->toEqual('Hello World');

toEqualCanonicalizing($expected)

Asserts that two variables have the same value. The contents of $value and $expected are canonicalized before they are compared. For instance, when the two variables are arrays, then these arrays are sorted before they are compared. When they are objects, each object is converted to an array containing all private, protected, and public attributes.

expect([4, 2, 1])->toEqualCanonicalizing([2, 4, 1]);

// this is the equivalent of:
expect([1, 2, 4])->toEqual([1, 2, 4]);

toEqualWithDelta($expected, float $delta)

Asserts that the absolute difference between $value and $expected is lower than $delta:

// Pass
expect(14)->toEqualWithDelta(10, 5);

// Fail
expect(14)->toEqualWithDelta(10, 0.1);

toBeInfinite()

Asserts that the value is infinite:

expect($universe)->toBeInfinite();

toBeInstanceOf($class)

Asserts that the value is an instance of $class:

expect($user)->toBeInstanceOf(User::class);

toBeArray()

Asserts that the value is an array:

expect($vegetables)->toBeArray();

toBeBool()

Asserts that the value is of type bool:

expect($isActive)->toBeBool();

toBeCallable()

Asserts that the value is of type callable:

expect($controller)->toBeCallable();

toBeFloat()

Asserts that the value is of type float:

expect($height)->toBeFloat();

toBeInt()

Asserts that the value is of type integer:

expect($count)->toBeInt();

toBeIterable()

Asserts that the value is of type iterable:

expect($value)->toBeIterable();

toBeNumeric()

Asserts that the value is of type numeric:

expect($age)->toBeNumeric();

toBeObject()

Asserts that the value is of type object:

expect($post)->toBeObject();

toBeResource()

Asserts that the value is of type resource:

expect($resource)->toBeResource();

toBeScalar()

Asserts that the value is of type scalar:

expect($scalar)->toBeScalar();

toBeString()

Asserts that the value is of type string:

expect($string)->toBeString();

toBeJson()

Asserts that the value is a JSON string:

expect('{"hello":"world"}')->toBeJson();

toBeNan()

Asserts that the value is not a number (NaN):

expect($nan)->toBeNan();

toBeNull()

Asserts that the value is null:

expect(null)->toBeNull();

toHaveKey(string $key)

Asserts that the value array contains the provided $key:

expect($array)->toHaveKey('key-a');

toHaveKeys(array $keys)

Asserts that the value array contains the provided $keys:

expect($array)->toHaveKeys(['key-a', 'key-b']);

toBeDirectory()

Asserts that the value is a directory:

expect($dir)->toBeDirectory();

toBeReadableDirectory()

Asserts that the value is a directory and is readable.

expect($dir)->toBeReadableDirectory();

toBeWritableDirectory()

Asserts that the value is a directory and is writable:

expect($dir)->toBeWritableDirectory();

toStartWith(string $expected)

Asserts that the value starts with the provided string:

expect($content)->toStartWith('Hello');

toMatch(string $expression)

Asserts that the value matches a regular expression:

expect('Hello World')->toMatch('/^hello wo.*$/i');

toEndWith(string $expected)

Asserts that the value ends with the provided string:

expect($content)->toEndWith('World');

toMatchConstraint(Constraint $constraint)

Asserts that the value matches a specified PHPUnit constraint:

use PHPUnit\Framework\Constraint\IsTrue;

expect(true)->toMatchConstraint(new IsTrue());

Asserts that the value matches a complex group of constraints:

use PHPUnit\Framework\Constraint\IsFalse;
use PHPUnit\Framework\Constraint\IsType;

expect(true)->toMatchConstraint(
    $this->logicalAnd(
         $this->logicalNot(new IsFalse()),
         new IsType(IsType::TYPE_BOOL)
    )
);

Asserts that the value matches a custom constraint:

expect('https://google.com')->toMatchConstraint(new IsValidUrlConstraint());
class IsValidUrlConstraint extends \PHPUnit\Framework\Constraint\Constraint
{
    public function toString()
    {
        return 'is a valid url';
    }
    protected function matches($other): bool
    {
        if (! is_string($other)) {
            return false;
        }
        return preg_match(
            Symfony\Component\Validator\Constraints\UrlValidator::PATTERN,
            $other
        ) > 0;
    }
}

Custom constraints should extend PHPUnit\Framework\Constraint\Constraint, and provide a matches() and toString() method, and optionally override the evaluate() method.

and($value)

Pass a new value to the and function to chain multiple expectations in a single test:

expect($id)->toBe(14)->and($name)->toBe('Nuno');

json()

Pass a JSON string to the json method to assert it is a valid JSON and chain other expectations:

expect('{"name":"Nuno","credit":1000.00}')
    ->json()
    ->toHaveCount(2)
    ->name->toBe('Nuno')
    ->credit->toBeFloat();

dd()

Use the dd method to dumps the current expectation value and ends the script:

expect(14)->dd(); // 14

expect([1, 2])->sequence(
    fn ($number) => $number->toBe(1),
    fn ($number) => $number->dd(), // 2
);

each()

Use the each modifier to create an expectation on each item of the given iterable:

expect([1, 2, 3])->each->toBeInt();
expect([1, 2, 3])->each->not->toBeString();

expect([1, 2, 3])->each(fn ($number) => $number->toBeLessThan(4));

not()

Use the not modifier before a check to invert it:

expect($id)->not->toBe(14);

sequence()

Use the sequence method to specify a sequential set of expectations for each item of the given iterable:

expect([1, 2, 3])->sequence(
    fn ($number) => $number->toBe(1),
    fn ($number) => $number->toBe(2),
    fn ($number) => $number->toBe(3),
);

You can also use the sequence method with associative iterables. Each closure receives the value as an expectation for the first argument, and the key as an expectation for the second argument:

expect(['hello' => 'world', 'foo' => 'bar', 'john' => 'doe'])->sequence(
    fn ($value, $key) => $value->toEqual('hello'),
    fn ($value, $key) => $key->toEqual('foo'),
    fn ($value, $key) => $value->toBeString(),
);

ray()

Use the ray method to debug the current expectation value with myray.app:

expect(14)->ray(); // 14

expect([1, 2])->sequence(
    fn ($number) => $number->toBe(1),
    fn ($number) => $number->ray(), // 2
);

Higher Order Expectations

You may create expectations on methods or properties of the original expectation value. As an example, imagine you're testing that a User can be created within your system. You might want to test that a variety of attributes have been stored correctly:

expect($user->first_name)->toEqual('Nuno');
expect($user->last_name)->toEqual('Maduro');
expect($user->withTitle('Mr'))->toEqual('Mr Nuno Maduro');

With higher order expectations, you can refactor that test to:

expect($user)
    ->first_name->toEqual('Nuno')
    ->last_name->toEqual('Maduro')
    ->withTitle('Mr')->toEqual('Mr Nuno Maduro');

You may also access array keys to perform expectations on them:

expect(['name' => 'Nuno', 'companies' => ['Pest', 'Laravel']])
    ->name->toEqual('Nuno')
    ->companies->toHaveCount(2)->each->toBeString

Pest takes care of retrieving the property or calling the method on the value under test.

Higher order expectations can be used with all of Pest's expectations. Which means you can create tests that are both powerful and elegant - even creating further higher order expectations within each() and sequence() closures:

expect($user)
    ->posts
    ->not->toBeEmpty
    ->toHaveCount(2)
    ->sequence(
        fn ($post) => $post->title->toEqual('My first post!'),
        fn ($post) => $post->title->toEqual('My second post')
    );

Custom Expectations

You can use expect()->extend() to add your own expectations to Pest. For example, let's say that you're testing a number utility library and you're frequently asserting that numbers appear within particular ranges of other numbers. You could abstract that into a toBeWithinRange expectation:

expect()->extend('toBeWithinRange', function ($min, $max) {
    return $this->toBeGreaterThanOrEqual($min)
                ->toBeLessThanOrEqual($max);
});

test('numeric ranges', function () {
    expect(100)->toBeWithinRange(90, 110);
});

Those custom expectations may be also placed in your tests/Pest.php file.


Next section: Setup And Teardown →