Pest vs PHPUnit Syntax: My Favorite expect() Examples

Pest testing framework has many fans, mainly because of its elegant, readable "English language" syntax. In this article, I will show you my 3 favorite expect() syntax examples, comparing the same things in Pest vs PHPUnit.

Notice: Generally, this topic is very opinionated, and the syntax is your personal preference. There is a Reddit post from a year ago where people argue against that Pest syntax. But I personally like it. Let me show you the examples.


1. expect() Object Properties in One Sentence

Let me just show you a real Pest example from open-source project Larastreamers by Christoph Rumpel:

it('can fetch channel details from youtube', function() {
// Act
$channel = YouTube::channel('UCdtd5QYBx9MUVXHm7qgEpxA');
 
// Assert
expect($channel)
->youTubeCustomUrl->toBe('christophrumpel')
->name->toBe('Christoph Rumpel')
->description->toStartWith('Hi, I\'m Christoph Rumpel')
->onPlatformSince->toIso8601String()->toBe('2010-01-12T19:15:29+00:00')
->country->toBe('AT')
->thumbnailUrl->toBe('https://yt3.ggpht.com/ytc/AAUvwniFZUkXnS4vDKY4lDohrpFsyu1V2AJwt4CFZGy25Q=s800-c-k-c0x00ffffff-no-rj');
});

See that expect() with chained methods?

If you have an object/array and want to check its parts for different values, you can do it in one sentence.

A similar functionality in PHPUnit would look like this:

public function testCanFetchChannelDetailsFromYoutube()
{
// Act
$channel = YouTube::channel('UCdtd5QYBx9MUVXHm7qgEpxA');
 
// Assert
$this->assertEquals('christophrumpel', $channel->youTubeCustomUrl);
$this->assertEquals('Christoph Rumpel', $channel->name);
$this->assertStringStartsWith('Hi, I\'m Christoph Rumpel', $channel->description);
$this->assertEquals('2010-01-12T19:15:29+00:00', $channel->onPlatformSince->toIso8601String());
$this->assertEquals('AT', $channel->country);
$this->assertEquals('https://yt3.ggpht.com/ytc/AAUvwniFZUkXnS4vDKY4lDohrpFsyu1V2AJwt4CFZGy25Q=s800-c-k-c0x00ffffff-no-rj', $channel->thumbnailUrl);
 
}

You can see three repeating parts here:

  • $this->
  • $channel->
  • and assertEquals() or even longer assertStringStartsWith()

Each assertion is a separate PHP statement.

Pest "chain" syntax is shorter and more elegant. Again, in my opinion.


2. Expect X AND Y

Another Pest example is checking the class and its contents in the same sentence.

test('users table contains pagination', function () {
$resp = actingAs($this->admin)
->get(route('users.index'));
 
$users = $resp->original->getData()['users'];
 
expect($users)
->toBeInstanceOf(\Illuminate\Pagination\LengthAwarePaginator::class)
->and($users->perPage())
->toBe(20);
});

The same code with PHPUnit:

public function testUsersTableContainsPagination()
{
$resp = $this->actingAs($this->admin)
->get(route('users.index'));
 
$users = $resp->original->getData()['users'];
 
$this->assertInstanceOf(\Illuminate\Pagination\LengthAwarePaginator::class, $users);
$this->assertEquals(20, $users->perPage());
}

In this case, it's not shorter but still more readable to me, as the assertion of the same variable is grouped in the same sentence instead of having two separate ones.


3. Expect (NOT) toContain()

Oh, and did I mention the syntax of the not with expect()?

Pest:

test('correct file can be seen on projects edit page', function () {
// ...
 
$resp = actingAs($this->user)
->get(route('projects.edit', $this->project));
 
expect($resp->content())
->toContain('project-file.pdf')
->not->toContain('task-file.pdf');
});

Two assertions in one sentence. Cool, right? Right?..

PHPUnit version:

public function testCorrectFileCanBeSeenOnProjectsEditPage()
{
$resp = $this->actingAs($user)
->get(route('projects.edit', $project));
 
$resp->assertSeeText('project-file.pdf');
$resp->assertDontSeeText('task-file.pdf');
}

It's personal preference, as I mentioned, but Pest syntax reads more "human" to me.

What do you think? Yay or Nay?


In Laravel Daily courses, we try to show code examples of both Pest and PHPUnit. Check them out:

No comments or questions yet...

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 68 courses (1183 lessons, total 43 h 18 min)
  • 90 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent New Courses