高阶测试

尽管“高阶测试”可能看起来是一个复杂的术语,但它实际上是一种简化测试的技术,并且完全是可选的。Pest 的核心理念之一是鼓励用户像关心其源代码一样关心其测试套件的美观和简洁性。因此,您可能会发现此技术很有趣,并选择将其应用于代码的某些部分。

让我们考虑一个演示如何将现有测试迁移到高阶测试的示例。为了说明,我们将使用一个简单的测试。

1it('works', function () {
2 $this->get('/')
3 ->assertStatus(200);
4});

根据此示例,您可以看到测试的全部内容是针对 $this 变量进行的链式调用。在这种情况下,可以完全消除测试闭包,并将所需的 method 直接链接到 it() 函数。

1it('works')
2 ->get('/')
3 ->assertStatus(200);

移除闭包函数并将测试体的 method 直接链接到 test()it() 函数的技术通常称为“高阶测试”。这种方法可以显著简化测试套件的代码。

此技术还可以与 断言 API 结合使用。让我们来看一个使用断言 API 验证用户是否以正确的名称创建的测试。

1it('has a name', function () {
2 $user = User::create([
3 'name' => 'Nuno Maduro',
4 ]);
5 
6 expect($user->name)->toBe('Nuno Maduro');
7});

如果您的测试只包含一个断言,我们可以使用高阶测试对其进行简化。

1it('has a name')
2 ->expect(fn () => User::create(['name' => 'Nuno Maduro'])->name)
3 ->toBe('Nuno Maduro');

务必通过将闭包传递给 expect() method 来使用断言值的惰性求值。这确保了只有在测试运行时才会创建期望值,而不是在此之前。

如果需要对需要在运行时惰性求值的 object 进行断言,可以使用 defer() method。

1it('creates admins')
2 ->defer(fn () => $this->artisan('user:create --admin'))
3 ->assertDatabaseHas('users', ['id' => 1]);

在上面的示例中,assertDatabaseHas() 断言 method 将在传递给 defer() method 的闭包的结果上调用。

高阶测试的原理也可以应用于钩子。这意味着,如果您的钩子的主体由一系列链接到 $this 变量的 method 组成,您可以简单地将这些 method 链接到钩子 method 并完全省略闭包。

1beforeEach(function () {
2 $this->withoutMiddleware();
3});
4 
5// Can be rewritten as...
6beforeEach()->withoutMiddleware();

使用高阶测试时,数据集值会为了方便而传递给 expect()defer() 闭包。

1it('validates emails')
3 ->expect(fn (string $email) => Validator::isValid($email))
4 ->toBeTrue();

高阶断言

使用高阶断言,您可以直接对断言 $value属性方法 执行断言。

例如,假设您正在测试用户是否已成功创建,并且各种属性已存储在数据库中。您的测试可能如下所示

1expect($user->name)->toBe('Nuno');
2expect($user->surname)->toBe('Maduro');
3expect($user->addTitle('Mr.'))->toBe('Mr. Nuno Maduro');

要利用高阶断言,您可以简单地将属性和方法直接链接到 expect() 函数,Pest 将负责检索被测 $value 上的属性值或调用方法。

现在,让我们看看重构为高阶断言的相同测试。

1expect($user)
2 ->name->toBe('Nuno')
3 ->surname->toBe('Maduro')
4 ->addTitle('Mr.')->toBe('Mr. Nuno Maduro');

处理数组时,您还可以访问 $value 数组键并在其上执行断言。

1expect(['name' => 'Nuno', 'projects' => ['Pest', 'OpenAI', 'Laravel Zero']])
2 ->name->toBe('Nuno')
3 ->projects->toHaveCount(3)
4 ->each->toBeString();
5 
6expect(['Dan', 'Luke', 'Nuno'])
7 ->{0}->toBe('Dan');

高阶断言可与所有 断言 一起使用,您甚至可以在闭包中创建更多的高阶断言。

1expect(['name' => 'Nuno', 'projects' => ['Pest', 'OpenAI', 'Laravel Zero']])
2 ->name->toBe('Nuno')
3 ->projects->toHaveCount(3)
4 ->sequence(
5 fn ($project) => $project->toBe('Pest'),
6 fn ($project) => $project->toBe('OpenAI'),
7 fn ($project) => $project->toBe('Laravel Zero'),
8 );

作用域高阶断言

使用作用域高阶断言,您可以使用 scoped() method 和闭包来访问并在链中的某个级别锁定断言。

这对于 Laravel Eloquent 模型非常有用,在这些模型中,您想检查子关系的属性。

1expect($user)
2->name->toBe('Nuno')
3->email->toBe('[email protected]')
4->address()->scoped(fn ($address) => $address
5 ->line1->toBe('1 Pest Street')
6 ->city->toBe('Lisbon')
7 ->country->toBe('Portugal')
8);

尽管高阶测试可能看起来很复杂,但它是一种可以显著简化测试套件代码的技术。在下一节中,我们将讨论 Pest 的社区视频资源:视频资源