高階測試

儘管「高階測試」乍看之下似乎是一個複雜的術語,但它實際上是一個簡化測試的技術,而且是完全可選的。Pest 的核心哲學之一是鼓勵使用者像關注原始碼一樣,關注測試套件的美感和簡潔。因此,你可能會覺得這個技術很有趣,並選擇在程式碼的某些部分採用它。

我們來考慮一個範例,說明如何將現有測試遷移到高階測試。為了說明,我們將使用一個簡單的測試。

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

根據這個範例,你可以看到測試的全部內容是在 $this 變數上進行的鏈式呼叫。在這種情況下,可以完全移除測試閉包,並將所需的方法直接鏈接到 it() 函式。

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

移除閉包函式並將測試主體的方法直接鏈接到 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() 方法對於預期結果值採用延遲評估至關重要。這能確保預期結果只在測試執行時建立,而不是之前。

如果你需要對在執行階段需要延遲評估的物件進行斷言,可以使用 defer() 方法。

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

在上面的範例中,assertDatabaseHas() 斷言方法將會在傳遞給 defer() 方法的閉包的結果上進行呼叫。

高階測試的原則也可以套用在勾子上。這表示如果勾子的主體包含一系列連結到變數 $this 的方法,你可以直接將這些方法連結到勾子方法,而完全省略封閉。

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

使用高階測試時,資料集值會為了方便起見傳遞到 expect()defer() 封閉。

1it('validates emails')
2 ->with(['taylor@laravel.com', 'enunomaduro@gmail.com'])
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() 方法和封閉來獲得存取權並鎖定鏈中的特定層級中以完成的期望。

這對於 Laravel Eloquent 模型相當有用,因為你可以針對子關聯檢查屬性。

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

儘管高階測試乍看之下可能會很複雜,但它是一種可以明顯簡化你的測試組代碼的技術。在下一章節中,我們將討論 Pest 社群的影片資源:影片資源