HTTP 測試
簡介
Laravel 提供了一個語義化的 API,這個 API 可以建立連到我們專案的 HTTP Request,並讓我們能加以檢查 Response。舉例來說,我們來看看下面定義的這個 Feature Test:
1<?php23namespace Tests\Feature;45use Illuminate\Foundation\Testing\RefreshDatabase;6use Illuminate\Foundation\Testing\WithoutMiddleware;7use Tests\TestCase;89class ExampleTest extends TestCase10{11 /**12 * A basic test example.13 */14 public function test_a_basic_request(): void15 {16 $response = $this->get('/');1718 $response->assertStatus(200);19 }20}
1<?php23namespace Tests\Feature;45use Illuminate\Foundation\Testing\RefreshDatabase;6use Illuminate\Foundation\Testing\WithoutMiddleware;7use Tests\TestCase;89class ExampleTest extends TestCase10{11 /**12 * A basic test example.13 */14 public function test_a_basic_request(): void15 {16 $response = $this->get('/');1718 $response->assertStatus(200);19 }20}
get
方法會建立連到專案的 GET
Request,而 assertStatus
方法則會判斷回傳的 Response 是否為給定的 HTTP 狀態碼。出了這種簡單的 Assertion 外,Laravel 也提供了各種不同的 Assertion,可用來檢查 Response 的 Header、內容、JSON 結構…等。
建立 Request
若要建立連到專案的 Request,可以在測試中叫用 get
、post
、put
、patch
、delete
方法。這些方法不會真的建立「真正的」HTTP Request,而是在程式內部模擬一段網路連線。
這些測試 Request 方法不是回傳 Illuminate\Http\Response
實體,而是回傳 Illuminate\Testing\TestResponse
的實體。TestResponse
實體提供了各種實用的 Assertion,可讓我們檢查專案的 Response:
1<?php23namespace Tests\Feature;45use Illuminate\Foundation\Testing\RefreshDatabase;6use Illuminate\Foundation\Testing\WithoutMiddleware;7use Tests\TestCase;89class ExampleTest extends TestCase10{11 /**12 * A basic test example.13 */14 public function test_a_basic_request(): void15 {16 $response = $this->get('/');1718 $response->assertStatus(200);19 }20}
1<?php23namespace Tests\Feature;45use Illuminate\Foundation\Testing\RefreshDatabase;6use Illuminate\Foundation\Testing\WithoutMiddleware;7use Tests\TestCase;89class ExampleTest extends TestCase10{11 /**12 * A basic test example.13 */14 public function test_a_basic_request(): void15 {16 $response = $this->get('/');1718 $response->assertStatus(200);19 }20}
一般來說,每個測試都應只向專案建立一個 Request。若在單一測試方法內建立多個 Request,可能會發生未預期的行為。
為了方便期間,在執行測試期間會自動禁用 CSRF Middleware。
自訂 Request 的 Header
可使用 withHeaders
方法來在 Request 傳送到專案前先自訂 Request 的 Header。使用這個方法,我們就可以自行加上任何需要的自定 Request:
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic functional test example.11 */12 public function test_interacting_with_headers(): void13 {14 $response = $this->withHeaders([15 'X-Header' => 'Value',16 ])->post('/user', ['name' => 'Sally']);1718 $response->assertStatus(201);19 }20}
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic functional test example.11 */12 public function test_interacting_with_headers(): void13 {14 $response = $this->withHeaders([15 'X-Header' => 'Value',16 ])->post('/user', ['name' => 'Sally']);1718 $response->assertStatus(201);19 }20}
Cookie
我們可以使用 withCookie
或 withCookies
方法來在建立 Request 前設定 Cookie 值。withCookie
方法有兩個引數:Cookie 名稱與 Cookie 值。withCookies
方法則接受一組名稱/值配對的陣列:
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 public function test_interacting_with_cookies(): void10 {11 $response = $this->withCookie('color', 'blue')->get('/');1213 $response = $this->withCookies([14 'color' => 'blue',15 'name' => 'Taylor',16 ])->get('/');17 }18}
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 public function test_interacting_with_cookies(): void10 {11 $response = $this->withCookie('color', 'blue')->get('/');1213 $response = $this->withCookies([14 'color' => 'blue',15 'name' => 'Taylor',16 ])->get('/');17 }18}
Session 與身份驗證
Laravel 提供了各種在 HTTP 測試期間處理 Session 的輔助函式。首先,我們可以使用給定 withSession
方法來以給定的陣列設定 Session 資料。若要在向專案傳送 Request 前先在 Session 內載入資料,就適合使用這個方法:
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 public function test_interacting_with_the_session(): void10 {11 $response = $this->withSession(['banned' => false])->get('/');12 }13}
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 public function test_interacting_with_the_session(): void10 {11 $response = $this->withSession(['banned' => false])->get('/');12 }13}
由於 Laravel 的 Session 通常是用來保存目前登入使用者的狀態,因此,也有一個 actingAs
輔助函式方法,可更簡單地讓我們將給定的使用者登入為目前使用者。舉例來說,我們可以使用 Model Factory 來產生並登入使用者:
1<?php23namespace Tests\Feature;45use App\Models\User;6use Tests\TestCase;78class ExampleTest extends TestCase9{10 public function test_an_action_that_requires_authentication(): void11 {12 $user = User::factory()->create();1314 $response = $this->actingAs($user)15 ->withSession(['banned' => false])16 ->get('/');17 }18}
1<?php23namespace Tests\Feature;45use App\Models\User;6use Tests\TestCase;78class ExampleTest extends TestCase9{10 public function test_an_action_that_requires_authentication(): void11 {12 $user = User::factory()->create();1314 $response = $this->actingAs($user)15 ->withSession(['banned' => false])16 ->get('/');17 }18}
只要在 actingAs
方法上傳入第二個引數,就可以指定要使用哪個 Guard 來登入給定的使用者。傳給 actingAs
方法的 Guard 會在該測試期間變成預設的 Guard:
1$this->actingAs($user, 'web')
1$this->actingAs($user, 'web')
為 Response 進行偵錯
向專案建立測試 Request 後,可使用 dump
、dumpHeaders
、dumpSession
方法來取得 Response 的內容或對其偵錯:
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic test example.11 */12 public function test_basic_test(): void13 {14 $response = $this->get('/');1516 $response->dumpHeaders();1718 $response->dumpSession();1920 $response->dump();21 }22}
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic test example.11 */12 public function test_basic_test(): void13 {14 $response = $this->get('/');1516 $response->dumpHeaders();1718 $response->dumpSession();1920 $response->dump();21 }22}
或者,我們可以使用 dd
、ddHeaders
、ddSession
方法來將該 Response 相關的資料傾印出來,並停止執行:
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic test example.11 */12 public function test_basic_test(): void13 {14 $response = $this->get('/');1516 $response->ddHeaders();1718 $response->ddSession();1920 $response->dd();21 }22}
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic test example.11 */12 public function test_basic_test(): void13 {14 $response = $this->get('/');1516 $response->ddHeaders();1718 $response->ddSession();1920 $response->dd();21 }22}
處理 Exception
有時候,我們可能會想測試專案是否有擲回特定的 Exception。為了避免該 Exception 被 Laravel 的 Exception Handler攔截並轉為 HTTP Response,請在建立 Request 前先叫用 withoutExceptionHandling
方法:
1$response = $this->withoutExceptionHandling()->get('/');
1$response = $this->withoutExceptionHandling()->get('/');
此外,若想確保專案中未使用到 PHP 或其他專案使用套件中宣告 Deprecated 的功能,我們可以在建立 Request 前叫用 withoutDeprecationHandling
方法。停用 Deprecation Handling 後,Deprecation Warning 會被轉換為 Exception,並導致測試執行失敗:
1$response = $this->withoutDeprecationHandling()->get('/');
1$response = $this->withoutDeprecationHandling()->get('/');
測試 JSON API
Laravel 中也提供了數種可用來測試 JSON API 與起 Response 的輔助函式。舉例來說,json
、getJson
、postJson
、putJson
、patchJson
、deleteJson
、optionsJson
等方法可用來以各種 HTTP Verb 來建立 JSON Request。我們也可以輕鬆地將資料與 Header 傳給這些方法。若要開始測試 JSON API,我們先來撰寫一個建立連到 /api/user
的 POST
Request,並撰寫預期回傳 JSON 資料的 Assertion:
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic functional test example.11 */12 public function test_making_an_api_request(): void13 {14 $response = $this->postJson('/api/user', ['name' => 'Sally']);1516 $response17 ->assertStatus(201)18 ->assertJson([19 'created' => true,20 ]);21 }22}
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic functional test example.11 */12 public function test_making_an_api_request(): void13 {14 $response = $this->postJson('/api/user', ['name' => 'Sally']);1516 $response17 ->assertStatus(201)18 ->assertJson([19 'created' => true,20 ]);21 }22}
此外,在 Response 上,我們可以用陣列變數的形式來存取 JSON Response 的資料。這麼一來我們就能方便地檢查 JSON Response 中回傳的各個值:
1$this->assertTrue($response['created']);
1$this->assertTrue($response['created']);
assertJson
方法會將 Response 轉換為陣列,並使用 PHPUnit::assertArraySubset
來驗證給定陣列是否有包含在專案回傳的 JSON Response 中。所以,如果在 JSON Response 中有包含其他屬性,只要給定的部分有包含在 JSON 裡,測試就會通過。
判斷 JSON 是否完全符合
剛才也提到過,assertJson
方法可用來判斷給定的部分 JSON 是否有包含在 JSON Response 中。若想檢查給定的陣列是否與專案回傳的 JSON 完全符合,請使用 assertExactJson
方法:
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic functional test example.11 */12 public function test_asserting_an_exact_json_match(): void13 {14 $response = $this->postJson('/user', ['name' => 'Sally']);1516 $response17 ->assertStatus(201)18 ->assertExactJson([19 'created' => true,20 ]);21 }22}
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic functional test example.11 */12 public function test_asserting_an_exact_json_match(): void13 {14 $response = $this->postJson('/user', ['name' => 'Sally']);1516 $response17 ->assertStatus(201)18 ->assertExactJson([19 'created' => true,20 ]);21 }22}
判斷 JSON 路徑
若想檢查 JSON Response 中,特定路徑上是否有包含給定資料,可使用 assertJsonPath
方法:
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic functional test example.11 */12 public function test_asserting_a_json_paths_value(): void13 {14 $response = $this->postJson('/user', ['name' => 'Sally']);1516 $response17 ->assertStatus(201)18 ->assertJsonPath('team.owner.name', 'Darian');19 }20}
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 /**10 * A basic functional test example.11 */12 public function test_asserting_a_json_paths_value(): void13 {14 $response = $this->postJson('/user', ['name' => 'Sally']);1516 $response17 ->assertStatus(201)18 ->assertJsonPath('team.owner.name', 'Darian');19 }20}
assertJsonPath
方法也接受一個閉包,該閉包可用於動態判斷該 Assertion 是否應設為通過:
1$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3);
1$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3);
Fluent JSON 測試
Laravel 也提供了另一種較好看、語義化的方法來讓我們測試專案的 JSON Response。若要用這種方法來測試 JSON Response,只需要傳入一個閉包給 assertJson
。在叫用這個閉包時,該閉包會收到一個 Illuminate\Testing\Fluent\AssertableJson
實體。AssertableJson
可用來對專案回傳的 JSON 做 Assertion。where
方法可用來對 JSON 中特定屬性做 Assertion。而 missing
方法可用來判斷 JSON 中是否不含特定屬性:
1use Illuminate\Testing\Fluent\AssertableJson;23/**4 * A basic functional test example.5 */6public function test_fluent_json(): void7{8 $response = $this->getJson('/users/1');910 $response11 ->assertJson(fn (AssertableJson $json) =>12 $json->where('id', 1)13 ->where('name', 'Victoria Faith')15 ->whereNot('status', 'pending')16 ->missing('password')17 ->etc()18 );19}
1use Illuminate\Testing\Fluent\AssertableJson;23/**4 * A basic functional test example.5 */6public function test_fluent_json(): void7{8 $response = $this->getJson('/users/1');910 $response11 ->assertJson(fn (AssertableJson $json) =>12 $json->where('id', 1)13 ->where('name', 'Victoria Faith')15 ->whereNot('status', 'pending')16 ->missing('password')17 ->etc()18 );19}
瞭解 etc
方法
在上述的範例中,讀者可能有注意到我們在 Assersion 串列的最後面叫用了 etc
方法。叫用該方法可讓 Laravel 知道在 JSON 物件中可能還有其他屬性。在沒有使用 etc
方法的情況下,若 JSON 物件中還有其他屬性存在,而我們未對這些屬性進行 Assersion 時,測試會執行失敗。
這種在沒有呼叫 etc
方法的情況下會使測試失敗的行為,是為了避免讓我們在 JSON Response 中不小心暴露出機敏資訊,所以才強制我們要針對所有屬性做 Assersion,或是使用 etc
方法來顯式允許其他額外的屬性。
不過,也請注意,若在 Assertion 中未串聯呼叫 etc
方法,是無法確保 JSON 物件中沒有被加入巢狀陣列。etc
方法只能確定在與呼叫 etc
方法相同的巢狀層級下沒有其他額外的屬性。
判斷屬性存在/不存在
若要判斷某個屬性存在或不存在,可使用 has
或 missing
方法:
1$response->assertJson(fn (AssertableJson $json) =>2 $json->has('data')3 ->missing('message')4);
1$response->assertJson(fn (AssertableJson $json) =>2 $json->has('data')3 ->missing('message')4);
此外,使用 hasAll
或 missingAll
方法,就可以同時針對多個屬性判斷存在或不存在:
1$response->assertJson(fn (AssertableJson $json) =>2 $json->hasAll(['status', 'data'])3 ->missingAll(['message', 'code'])4);
1$response->assertJson(fn (AssertableJson $json) =>2 $json->hasAll(['status', 'data'])3 ->missingAll(['message', 'code'])4);
我們可以使用 hasAny
方法來判斷給定屬性列表中是否至少有一個屬性存在:
1$response->assertJson(fn (AssertableJson $json) =>2 $json->has('status')3 ->hasAny('data', 'message', 'code')4);
1$response->assertJson(fn (AssertableJson $json) =>2 $json->has('status')3 ->hasAny('data', 'message', 'code')4);
判斷 JSON Collection
通常來說,在 Route 中回傳的 Json Response 會包含多個項目,如多位使用者:
1Route::get('/users', function () {2 return User::all();3});
1Route::get('/users', function () {2 return User::all();3});
在這些情況下,我們可以使用 Fluent JSON 物件的 has
方法來針對該 Response 中的使用者進行 Assertion。舉例來說,我們來判斷 JSON Response 中是否有包含三位使用者。接著,我們再使用 first
方法來對該 Collection 中的使用者做 Assertion。first
方法接受一個閉包,該閉包會收到另一個可 Assert 的 JSON 字串,我們可以使用這個 JSON 字串來針對該 JSON Collection 中的第一個物件進行 Assertion:
1$response2 ->assertJson(fn (AssertableJson $json) =>3 $json->has(3)4 ->first(fn (AssertableJson $json) =>5 $json->where('id', 1)6 ->where('name', 'Victoria Faith')8 ->missing('password')9 ->etc()10 )11 );
1$response2 ->assertJson(fn (AssertableJson $json) =>3 $json->has(3)4 ->first(fn (AssertableJson $json) =>5 $json->where('id', 1)6 ->where('name', 'Victoria Faith')8 ->missing('password')9 ->etc()10 )11 );
限定範圍的 JSON Collection Assertion
有時候,Route 可能會回傳被指派為命名索引鍵的 JSON Collection:
1Route::get('/users', function () {2 return [3 'meta' => [...],4 'users' => User::all(),5 ];6})
1Route::get('/users', function () {2 return [3 'meta' => [...],4 'users' => User::all(),5 ];6})
在測試這類 Route 時,可以使用 has
方法來判斷該 Collection 中的項目數。此外,也可以使用 has
方法來在一連串的 Assertion 間限制判斷的範圍:
1$response2 ->assertJson(fn (AssertableJson $json) =>3 $json->has('meta')4 ->has('users', 3)5 ->has('users.0', fn (AssertableJson $json) =>6 $json->where('id', 1)7 ->where('name', 'Victoria Faith')9 ->missing('password')10 ->etc()11 )12 );
1$response2 ->assertJson(fn (AssertableJson $json) =>3 $json->has('meta')4 ->has('users', 3)5 ->has('users.0', fn (AssertableJson $json) =>6 $json->where('id', 1)7 ->where('name', 'Victoria Faith')9 ->missing('password')10 ->etc()11 )12 );
不過,除了一次對 users
Collection 呼叫兩次 has
方法以外,我們也可以只呼叫一次,並提供一個閉包作為該方法的第三個引數。傳入閉包時,Laravel 會自動叫用該閉包,並將作用範圍限定在該 Collection 的第一個項目:
1$response2 ->assertJson(fn (AssertableJson $json) =>3 $json->has('meta')4 ->has('users', 3, fn (AssertableJson $json) =>5 $json->where('id', 1)6 ->where('name', 'Victoria Faith')8 ->missing('password')9 ->etc()10 )11 );
1$response2 ->assertJson(fn (AssertableJson $json) =>3 $json->has('meta')4 ->has('users', 3, fn (AssertableJson $json) =>5 $json->where('id', 1)6 ->where('name', 'Victoria Faith')8 ->missing('password')9 ->etc()10 )11 );
判斷 JSON 型別
我們可能會想檢查 JSON Response 中的某些屬性是否為特定的型別。Illuminate\Testing\Fluent\AssertableJson
類別中,提供了 whereType
與 whereAllType
方法可讓我們檢查 JSON 屬性中的型別:
1$response->assertJson(fn (AssertableJson $json) =>2 $json->whereType('id', 'integer')3 ->whereAllType([4 'users.0.name' => 'string',5 'meta' => 'array'6 ])7);
1$response->assertJson(fn (AssertableJson $json) =>2 $json->whereType('id', 'integer')3 ->whereAllType([4 'users.0.name' => 'string',5 'meta' => 'array'6 ])7);
我們也可以使用 |
字元來指定多個型別,或者,也可以傳入一組型別陣列作為 whereType
的第二個引數。若 Response 值符合任意列出的型別,則該 Assertion 會執行成功:
1$response->assertJson(fn (AssertableJson $json) =>2 $json->whereType('name', 'string|null')3 ->whereType('id', ['string', 'integer'])4);
1$response->assertJson(fn (AssertableJson $json) =>2 $json->whereType('name', 'string|null')3 ->whereType('id', ['string', 'integer'])4);
whereType
與 whereAllType
方法可支援下列型別: string
、integer
、double
、boolean
、array
、null
。
測試檔案上傳
Illuminate\Http\UploadedFile
類別提供了一個 fake
方法,可用來產生用於測試的假檔案或圖片。只要將 UploadedFile
的 fake
方法與 Storage
Facade 的 fake
方法一起使用,我們就能大幅簡化測試檔案上傳的過程。舉例來說,我們可以將這兩個功能搭配使用,來測試某個上傳使用者大頭照的表單:
1<?php23namespace Tests\Feature;45use Illuminate\Foundation\Testing\RefreshDatabase;6use Illuminate\Foundation\Testing\WithoutMiddleware;7use Illuminate\Http\UploadedFile;8use Illuminate\Support\Facades\Storage;9use Tests\TestCase;1011class ExampleTest extends TestCase12{13 public function test_avatars_can_be_uploaded(): void14 {15 Storage::fake('avatars');1617 $file = UploadedFile::fake()->image('avatar.jpg');1819 $response = $this->post('/avatar', [20 'avatar' => $file,21 ]);2223 Storage::disk('avatars')->assertExists($file->hashName());24 }25}
1<?php23namespace Tests\Feature;45use Illuminate\Foundation\Testing\RefreshDatabase;6use Illuminate\Foundation\Testing\WithoutMiddleware;7use Illuminate\Http\UploadedFile;8use Illuminate\Support\Facades\Storage;9use Tests\TestCase;1011class ExampleTest extends TestCase12{13 public function test_avatars_can_be_uploaded(): void14 {15 Storage::fake('avatars');1617 $file = UploadedFile::fake()->image('avatar.jpg');1819 $response = $this->post('/avatar', [20 'avatar' => $file,21 ]);2223 Storage::disk('avatars')->assertExists($file->hashName());24 }25}
若想檢查給定的檔案是否不存在,可使用 Storage
Facade 的 assertMissing
方法:
1Storage::fake('avatars');23// ...45Storage::disk('avatars')->assertMissing('missing.jpg');
1Storage::fake('avatars');23// ...45Storage::disk('avatars')->assertMissing('missing.jpg');
自訂 Fake 檔案
在使用 UploadedFile
類別的 fake
方法來建立檔案時,我們可以指定圖片的長寬與檔案大小 (單位為 kB),以更好地測試程式中的表單驗證規則:
1UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);
1UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);
除了建立圖片外,還可以使用 create
方法來建立任何其他類型的檔案:
1// $sizeInKilobytes = 單位為 kB 的檔案大小2UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);
1// $sizeInKilobytes = 單位為 kB 的檔案大小2UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);
若有需要,可傳入 $mimeType
引數,以明顯定義 File 要回傳的 MIME 型別:
1UploadedFile::fake()->create(2 'document.pdf', $sizeInKilobytes, 'application/pdf'3);
1UploadedFile::fake()->create(2 'document.pdf', $sizeInKilobytes, 'application/pdf'3);
測試 View
在 Laravel 中,我們也可以在不模擬 HTTP Request 的情況轉譯 View。若要在不模擬 HTTP Request 的情況下轉譯 View,我們可以在測試中呼叫 view
方法。view
方法的參數為 View 的名稱,以及一組可選的資料陣列。該方法會回傳 Illuminate\Testing\TestView
的實體,使用 TestView
,我們就能方便地針對 View 的內容進行 Assertion:
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 public function test_a_welcome_view_can_be_rendered(): void10 {11 $view = $this->view('welcome', ['name' => 'Taylor']);1213 $view->assertSee('Taylor');14 }15}
1<?php23namespace Tests\Feature;45use Tests\TestCase;67class ExampleTest extends TestCase8{9 public function test_a_welcome_view_can_be_rendered(): void10 {11 $view = $this->view('welcome', ['name' => 'Taylor']);1213 $view->assertSee('Taylor');14 }15}
TestView
類別還提供了下列 Assertion 方法:assertSee
、assertSeeInOrder
、assertSeeText
、assertSeeTextInOrder
、assertDontSee
、assertDontSeeText
。
若有需要,我們可以將 TestView
實體轉換型別為字串來取得原始的 View 內容轉譯結果:
1$contents = (string) $this->view('welcome');
1$contents = (string) $this->view('welcome');
共用錯誤訊息
有的 View 會使用到 Laravel 所提供的全域 Error Bag中共享的錯誤。若要在全域 Error Bag 中填充錯誤訊息,可使用 withViewErrors
方法:
1$view = $this->withViewErrors([2 'name' => ['Please provide a valid name.']3])->view('form');45$view->assertSee('Please provide a valid name.');
1$view = $this->withViewErrors([2 'name' => ['Please provide a valid name.']3])->view('form');45$view->assertSee('Please provide a valid name.');
轉譯 Blade 與元件
若有需要,可以使用 blade
方法來取值並轉譯原始的 Blade 字串。與 view
方法類似,blade
方法也會回傳 Illuminate\Testing\TestView
的實體:
1$view = $this->blade(2 '<x-component :name="$name" />',3 ['name' => 'Taylor']4);56$view->assertSee('Taylor');
1$view = $this->blade(2 '<x-component :name="$name" />',3 ['name' => 'Taylor']4);56$view->assertSee('Taylor');
可以使用 component
方法來取值並轉譯 Blade 元件。component
元件也會回傳 Illuminate\Testing\TestComponent
的實體:
1$view = $this->component(Profile::class, ['name' => 'Taylor']);23$view->assertSee('Taylor');
1$view = $this->component(Profile::class, ['name' => 'Taylor']);23$view->assertSee('Taylor');
可用的 Assertion
Response 的 Assertion
Laravel 的 Illuminate\Testing\TestResponse
類別提供了各種自訂 Assertion 方法供我們在測試程式時使用。這些 Assertion 可在 json
、get
、post
、put
、delete
測試方法回傳的 Response 上存取:
assertCookie assertCookieExpired assertCookieNotExpired assertCookieMissing assertCreated assertDontSee assertDontSeeText assertDownload assertExactJson assertForbidden assertHeader assertHeaderMissing assertJson assertJsonCount assertJsonFragment assertJsonIsArray assertJsonIsObject assertJsonMissing assertJsonMissingExact assertJsonMissingValidationErrors assertJsonPath assertJsonMissingPath assertJsonStructure assertJsonValidationErrors assertJsonValidationErrorFor assertLocation assertContent assertNoContent assertStreamedContent assertNotFound assertOk assertPlainCookie assertRedirect assertRedirectContains assertRedirectToRoute assertRedirectToSignedRoute assertSee assertSeeInOrder assertSeeText assertSeeTextInOrder assertSessionHas assertSessionHasInput assertSessionHasAll assertSessionHasErrors assertSessionHasErrorsIn assertSessionHasNoErrors assertSessionDoesntHaveErrors assertSessionMissing assertStatus assertSuccessful assertUnauthorized assertUnprocessable assertValid assertInvalid assertViewHas assertViewHasAll assertViewIs assertViewMissing
assertCookie
判斷 Response 包含給定 Cookie:
1$response->assertCookie($cookieName, $value = null);
1$response->assertCookie($cookieName, $value = null);
assertCookieExpired
判斷 Response 包含給定 Cookie,且該 Cookie 已逾期:
1$response->assertCookieExpired($cookieName);
1$response->assertCookieExpired($cookieName);
assertCookieNotExpired
判斷 Response 包含給定 Cookie,且該 Cookie 未逾期:
1$response->assertCookieNotExpired($cookieName);
1$response->assertCookieNotExpired($cookieName);
assertCookieMissing
判斷 Response 不包含給定 Cookie:
1$response->assertCookieMissing($cookieName);
1$response->assertCookieMissing($cookieName);
assertCreated
判斷 Response 是否為 201 HTTP 狀態碼:
1$response->assertCreated();
1$response->assertCreated();
assertDontSee
判斷程式回傳的 Response 中是否不包含給定字串。除非將第二個引數設為 false
,否則該 Assertion 會自動逸出給定的字串:
1$response->assertDontSee($value, $escaped = true);
1$response->assertDontSee($value, $escaped = true);
assertDontSeeText
判斷 Response 的文字中是否不包含給定字串。除非將第二個引數設為 false
,否則該 Assertion 會自動逸出給定的字串。該方法會將 Response 的內容傳給 strip_tags
PHP 函式,然後再進行判斷:
1$response->assertDontSeeText($value, $escaped = true);
1$response->assertDontSeeText($value, $escaped = true);
assertDownload
判斷 Response 是否為「檔案下載」。一般來說,就表示所叫用的 Route 回傳了 Response::download
Response、BinaryFileResponse
、 或是 Storage::download
Response:
1$response->assertDownload();
1$response->assertDownload();
若有需要,可判斷該下載檔案是否為給定的檔名:
1$response->assertDownload('image.jpg');
1$response->assertDownload('image.jpg');
assertExactJson
判斷 Response 是否包含與給定的 JSON 資料完全相符的內容:
1$response->assertExactJson(array $data);
1$response->assertExactJson(array $data);
assertForbidden
判斷 Response 是否為 Forbidden (403) HTTP 狀態碼:
1$response->assertForbidden();
1$response->assertForbidden();
assertHeader
判斷 Response 中是否有給定的 Header 與值:
1$response->assertHeader($headerName, $value = null);
1$response->assertHeader($headerName, $value = null);
assertHeaderMissing
判斷 Response 中是否不含給定的 Header:
1$response->assertHeaderMissing($headerName);
1$response->assertHeaderMissing($headerName);
assertJson
判斷 Response 是否包含給定的 JSON 資料:
1$response->assertJson(array $data, $strict = false);
1$response->assertJson(array $data, $strict = false);
assertJson
方法會將 Response 轉換為陣列,並使用 PHPUnit::assertArraySubset
來驗證給定陣列是否有包含在專案回傳的 JSON Response 中。所以,如果在 JSON Response 中有包含其他屬性,只要給定的部分有包含在 JSON 裡,測試就會通過。
assertJsonCount
判斷 Response JSON 是否為一組陣列,且該陣列在給定的索引鍵上有包含預期數量的項目:
1$response->assertJsonCount($count, $key = null);
1$response->assertJsonCount($count, $key = null);
assertJsonFragment
判斷 Response 中,是否有在任意位置上包含給定的 JSON 資料:
1Route::get('/users', function () {2 return [3 'users' => [4 [5 'name' => 'Taylor Otwell',6 ],7 ],8 ];9});1011$response->assertJsonFragment(['name' => 'Taylor Otwell']);
1Route::get('/users', function () {2 return [3 'users' => [4 [5 'name' => 'Taylor Otwell',6 ],7 ],8 ];9});1011$response->assertJsonFragment(['name' => 'Taylor Otwell']);
assertJsonIsArray
判斷 Response JSON 為一陣列:
1$response->assertJsonIsArray();
1$response->assertJsonIsArray();
assertJsonIsObject
判斷 Response JSON 為一物件:
1$response->assertJsonIsObject();
1$response->assertJsonIsObject();
assertJsonMissing
判斷 Response 中是否不包含給定的 JSON 資料:
1$response->assertJsonMissing(array $data);
1$response->assertJsonMissing(array $data);
assertJsonMissingExact
判斷 Response 是否不包含完全相符的 JSON 資料:
1$response->assertJsonMissingExact(array $data);
1$response->assertJsonMissingExact(array $data);
assertJsonMissingValidationErrors
判斷 Response 中,給定的索引鍵上是否不含 JSON 驗證錯誤:
1$response->assertJsonMissingValidationErrors($keys);
1$response->assertJsonMissingValidationErrors($keys);
還有一個更泛用的 assertValid 方法,可用來檢查 Response 是否不含以 JSON 格式回傳的驗證錯誤,並檢查 Session Storage 上是否未有快閃存入錯誤訊息。
assertJsonPath
判斷 Response 中在特定路徑上是否包含給定的資料:
1$response->assertJsonPath($path, $expectedValue);
1$response->assertJsonPath($path, $expectedValue);
舉例來說,若程式回傳下列 JSON Response:
1{2 "user": {3 "name": "Steve Schoger"4 }5}
1{2 "user": {3 "name": "Steve Schoger"4 }5}
則我們可以像這樣判斷 user
物件的 name
屬性是否符合給定的值:
1$response->assertJsonPath('user.name', 'Steve Schoger');
1$response->assertJsonPath('user.name', 'Steve Schoger');
assertJsonMissingPath
判斷 Response 是否不包含給定的路徑:
1$response->assertJsonMissingPath($path);
1$response->assertJsonMissingPath($path);
舉例來說,若程式回傳下列 JSON Response:
1{2 "user": {3 "name": "Steve Schoger"4 }5}
1{2 "user": {3 "name": "Steve Schoger"4 }5}
可以判斷 JSON 是否不包含 user
物件的 email
屬性:
1$response->assertJsonMissingPath('user.email');
1$response->assertJsonMissingPath('user.email');
assertJsonStructure
判斷 Response 是否含有給定的 JSON 結構:
1$response->assertJsonStructure(array $structure);
1$response->assertJsonStructure(array $structure);
舉例來說,若程式回傳了包含下列資料的 JSON Response:
1{2 "user": {3 "name": "Steve Schoger"4 }5}
1{2 "user": {3 "name": "Steve Schoger"4 }5}
也可以像這樣檢查 JSON 結構是否符合預期:
1$response->assertJsonStructure([2 'user' => [3 'name',4 ]5]);
1$response->assertJsonStructure([2 'user' => [3 'name',4 ]5]);
有時候,程式會回傳的 JSON Response 會包含一組由物件組成的陣列:
1{2 "user": [3 {4 "name": "Steve Schoger",5 "age": 55,6 "location": "Earth"7 },8 {9 "name": "Mary Schoger",10 "age": 60,11 "location": "Earth"12 }13 ]14}
1{2 "user": [3 {4 "name": "Steve Schoger",5 "age": 55,6 "location": "Earth"7 },8 {9 "name": "Mary Schoger",10 "age": 60,11 "location": "Earth"12 }13 ]14}
這時,我們可以使用 *
字元來對該陣列中的所有物件進行結構檢查:
1$response->assertJsonStructure([2 'user' => [3 '*' => [4 'name',5 'age',6 'location'7 ]8 ]9]);
1$response->assertJsonStructure([2 'user' => [3 '*' => [4 'name',5 'age',6 'location'7 ]8 ]9]);
assertJsonValidationErrors
判斷 Response 中,給定的索引鍵上是否有給定的 JSON 驗證錯誤。該方法應用於檢查以 JSON 格式回傳的表單驗證錯誤 Response,而不應用於檢查快閃存入 Session 中的表單驗證錯誤:
1$response->assertJsonValidationErrors(array $data, $responseKey = 'errors');
1$response->assertJsonValidationErrors(array $data, $responseKey = 'errors');
還有一個更泛用的 assertInvalid 方法,可用來檢查 Response 是否包含以 JSON 格式回傳的驗證錯誤,或是 Session Storage 上是否有快閃存入錯誤訊息。
assertJsonValidationErrorFor
判斷 Response 中,給定的索引鍵上是否有任何的 JSON 驗證規則:
1$response->assertJsonValidationErrorFor(string $key, $responseKey = 'errors');
1$response->assertJsonValidationErrorFor(string $key, $responseKey = 'errors');
assertLocation
判斷 Response 中,Location
Header 上是否有給定的 URI 值:
1$response->assertLocation($uri);
1$response->assertLocation($uri);
assertContent
判斷給定字串符合 Response 的內容:
1$response->assertContent($value);
1$response->assertContent($value);
assertNoContent
判斷 Response 是否為給定的 HTTP 狀態碼,且不含內容:
1$response->assertNoContent($status = 204);
1$response->assertNoContent($status = 204);
assertStreamedContent
判斷給定字串符合 Stream 的 Response 內容:
1$response->assertStreamedContent($value);
1$response->assertStreamedContent($value);
assertNotFound
判斷 Response 是否為 Not Found (404) HTTP 狀態碼:
1$response->assertNotFound();
1$response->assertNotFound();
assertOk
判斷 Response 是否為 200 HTTP 狀態碼:
1$response->assertOk();
1$response->assertOk();
assertPlainCookie
判斷 Response 是否包含給定未加密的 Cookie:
1$response->assertPlainCookie($cookieName, $value = null);
1$response->assertPlainCookie($cookieName, $value = null);
assertRedirect
判斷 Response 是否為指向給定 URI 的重新導向:
1$response->assertRedirect($uri);
1$response->assertRedirect($uri);
assertRedirectContains
判斷 Response 是否在重新導向至包含給定字串的 URI:
1$response->assertRedirectContains($string);
1$response->assertRedirectContains($string);
assertRedirectToRoute
判斷 Response 是否重新導向到給定的命名 Route:
1$response->assertRedirectToRoute($name = null, $parameters = []);
1$response->assertRedirectToRoute($name = null, $parameters = []);
assertRedirectToSignedRoute
判斷 Response 是否重新導向到給定的簽名 Route:
1$response->assertRedirectToSignedRoute($name = null, $parameters = []);
1$response->assertRedirectToSignedRoute($name = null, $parameters = []);
assertSee
判斷 Response 中是否包含給定字串。除非將第二個引數設為 false
,否則該 Assertion 會自動逸出給定的字串:
1$response->assertSee($value, $escaped = true);
1$response->assertSee($value, $escaped = true);
assertSeeInOrder
判斷 Response 中,是否有依照順序包含給定字串。除非將第二個引數設為 false
,否則該 Assertion 會自動逸出給定的字串:
1$response->assertSeeInOrder(array $values, $escaped = true);
1$response->assertSeeInOrder(array $values, $escaped = true);
assertSeeText
判斷 Response 的文字中是否包含給定字串。除非將第二個引數設為 false
,否則該 Assertion 會自動逸出給定的字串。該方法會將 Response 的內容傳給 strip_tags
PHP 函式,然後再進行判斷:
1$response->assertSeeText($value, $escaped = true);
1$response->assertSeeText($value, $escaped = true);
assertSeeTextInOrder
判斷 Response 的文字中,是否有依照順序出現給定的字串。除非將第二個引數設為 false
,否則該 Assertion 會自動逸出給定的字串。該方法會將 Response 的內容傳給 strip_tags
PHP 函式,然後再進行判斷:
1$response->assertSeeTextInOrder(array $values, $escaped = true);
1$response->assertSeeTextInOrder(array $values, $escaped = true);
assertSessionHas
判斷 Session 是否包含給定的資料:
1$response->assertSessionHas($key, $value = null);
1$response->assertSessionHas($key, $value = null);
若有需要,assertSessionHas
方法的第二個引數也可以傳入閉包。若閉包回傳 true
,則會視為 Assertion 通過:
1$response->assertSessionHas($key, function (User $value) {2 return $value->name === 'Taylor Otwell';3});
1$response->assertSessionHas($key, function (User $value) {2 return $value->name === 'Taylor Otwell';3});
assertSessionHasInput
判斷 Session 中快閃的輸入陣列內是否包含給定值:
1$response->assertSessionHasInput($key, $value = null);
1$response->assertSessionHasInput($key, $value = null);
若有需要若有需要,assertSessionHasInput
方法的第二個引數也可以傳入閉包。若閉包回傳 true
,則會視為 Assertion 通過:
1$response->assertSessionHasInput($key, function (string $value) {2 return Crypt::decryptString($value) === 'secret';3});
1$response->assertSessionHasInput($key, function (string $value) {2 return Crypt::decryptString($value) === 'secret';3});
assertSessionHasAll
判斷 Session 內是否包含給定的索引鍵/值配對陣列:
1$response->assertSessionHasAll(array $data);
1$response->assertSessionHasAll(array $data);
舉例來說,若網站的 Session 內有 name
與 status
索引鍵,則可以像這樣來判斷這兩個欄位是否都存在且為特定的值:
1$response->assertSessionHasAll([2 'name' => 'Taylor Otwell',3 'status' => 'active',4]);
1$response->assertSessionHasAll([2 'name' => 'Taylor Otwell',3 'status' => 'active',4]);
assertSessionHasErrors
判斷 Session 內給定的 $keys
中是否有錯誤。若``$keys` 為關聯式陣列,則會判斷 Session 中,各個欄位 (陣列索引鍵) 是否有特定的錯誤訊息 (陣列值)。請勿使用該方法來測試會回傳 JSON 結構的 Route,請使用該方法來測試將驗證錯誤訊息快閃存入 Session 的 Route:
1$response->assertSessionHasErrors(2 array $keys, $format = null, $errorBag = 'default'3);
1$response->assertSessionHasErrors(2 array $keys, $format = null, $errorBag = 'default'3);
舉例來說,若要判斷 name
與 email
欄位中是否有快閃存入 Session 的驗證錯誤訊息,可像這樣叫用 assertSessionHasErrors
方法:
1$response->assertSessionHasErrors(['name', 'email']);
1$response->assertSessionHasErrors(['name', 'email']);
或者,也可以判斷給定欄位是否有特定的驗證錯誤訊息:
1$response->assertSessionHasErrors([2 'name' => 'The given name was invalid.'3]);
1$response->assertSessionHasErrors([2 'name' => 'The given name was invalid.'3]);
還有一個更泛用的 assertInvalid 方法,可用來檢查 Response 是否包含以 JSON 格式回傳的驗證錯誤,或是 Session Storage 上是否有快閃存入錯誤訊息。
assertSessionHasErrorsIn
判斷 Session 中,在特定的 Error Bag 中,給定的 $keys
內是否有錯誤訊息。若 $keys
為關聯式陣列,則會判斷 Session 中各個欄位 (陣列索引鍵) 是否有特定的錯誤訊息 (陣列值):
1$response->assertSessionHasErrorsIn($errorBag, $keys = [], $format = null);
1$response->assertSessionHasErrorsIn($errorBag, $keys = [], $format = null);
assertSessionHasNoErrors
判斷 Session 中是否無驗證錯誤:
1$response->assertSessionHasNoErrors();
1$response->assertSessionHasNoErrors();
assertSessionDoesntHaveErrors
判斷 Session 中給定的索引鍵是否無驗證錯誤:
1$response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default');
1$response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default');
還有一個更泛用的 assertValid 方法,可用來檢查 Response 是否不含以 JSON 格式回傳的驗證錯誤,並檢查 Session Storage 上是否未有快閃存入錯誤訊息。
assertSessionMissing
判斷 Session 中是否不包含給定的索引鍵:
1$response->assertSessionMissing($key);
1$response->assertSessionMissing($key);
assertStatus
判斷 Response 是否回傳給定的 HTTP 狀態碼:
1$response->assertStatus($code);
1$response->assertStatus($code);
assertSuccessful
判斷 Response 是否有成功的 HTTP 狀態碼 (>= 200 且 < 300):
1$response->assertSuccessful();
1$response->assertSuccessful();
assertUnauthorized
判斷 Response 是否為禁止存取 (401) HTTP 狀態碼:
1$response->assertUnauthorized();
1$response->assertUnauthorized();
assertUnprocessable
判斷 Response 是否為無法處理 (422) HTTP 狀態碼:
1$response->assertUnprocessable();
1$response->assertUnprocessable();
assertValid
判斷 Response 中,不包含給定索引鍵的驗證錯誤。該方法可用來判斷以 JSON 結構回傳驗證錯誤,或是將驗證錯誤快閃存入 Session 的 Response:
1// 判斷無驗證錯誤訊息...2$response->assertValid();34// 判斷給定的索引鍵中沒有給定的驗證錯誤訊息...5$response->assertValid(['name', 'email']);
1// 判斷無驗證錯誤訊息...2$response->assertValid();34// 判斷給定的索引鍵中沒有給定的驗證錯誤訊息...5$response->assertValid(['name', 'email']);
assertInvalid
判斷 Response 中,給定的索引鍵是否有驗證錯誤訊息。該方法可用於檢查以 JSON 格式回傳錯誤訊息,或是將驗證錯誤訊息快閃存入 Session 的 Response:
1$response->assertInvalid(['name', 'email']);
1$response->assertInvalid(['name', 'email']);
也可以判斷給定的索引鍵是否有特定的錯誤訊息。在判斷是否有特定的錯誤訊息時,可提供完整的訊息,或是其中一段的錯誤訊息:
1$response->assertInvalid([2 'name' => 'The name field is required.',3 'email' => 'valid email address',4]);
1$response->assertInvalid([2 'name' => 'The name field is required.',3 'email' => 'valid email address',4]);
assertViewHas
判斷 Response View 中是否包含給定的一部分資料:
1$response->assertViewHas($key, $value = null);
1$response->assertViewHas($key, $value = null);
若在 assertViewHas
方法中的第二個引數傳入閉包,則可檢查並針對一部分的資料做 Assertion:
1$response->assertViewHas('user', function (User $user) {2 return $user->name === 'Taylor';3});
1$response->assertViewHas('user', function (User $user) {2 return $user->name === 'Taylor';3});
此外,也可以在 Response 上以陣列變數的形式來存取 View Data,讓我們可以方便地檢查這些值:
1$this->assertEquals('Taylor', $response['name']);
1$this->assertEquals('Taylor', $response['name']);
assertViewHasAll
判斷 Response View 中是否包含一組資料:
1$response->assertViewHasAll(array $data);
1$response->assertViewHasAll(array $data);
該方法可用於檢查 View 中是否含有符合給定索引鍵的資料:
1$response->assertViewHasAll([2 'name',3 'email',4]);
1$response->assertViewHasAll([2 'name',3 'email',4]);
或者,也可以判斷是否包含特定的 View Data,且這些資料是否為指定的值:
1$response->assertViewHasAll([2 'name' => 'Taylor Otwell',4]);
1$response->assertViewHasAll([2 'name' => 'Taylor Otwell',4]);
assertViewIs
判斷 Route 是否回傳給定的 View:
1$response->assertViewIs($value);
1$response->assertViewIs($value);
assertViewMissing
判斷程式回傳的 Reponse 中,是否不含給定的資料索引鍵:
1$response->assertViewMissing($key);
1$response->assertViewMissing($key);
身份驗證 Assertion
Laravel 也提供了各種與身份驗證相關的 Assertion,讓我們能在專案的 Feature Test 中使用。請注意,這些方法需要在 Test Class 上呼叫,而不是在 get
或 post
方法回傳的 Illuminate\Testing\TestResponse
實體上呼叫。
assertAuthenticated
判斷使用者已登入:
1$this->assertAuthenticated($guard = null);
1$this->assertAuthenticated($guard = null);
assertGuest
判斷使用者是否未登入:
1$this->assertGuest($guard = null);
1$this->assertGuest($guard = null);
assertAuthenticatedAs
判斷是否已登入特定的使用者:
1$this->assertAuthenticatedAs($user, $guard = null);
1$this->assertAuthenticatedAs($user, $guard = null);
Validation 的 Assertion
Laravel 提供的 Assertion 中,主要有兩種是用來針對 Validation 判斷的,可用來確保 Request 中的資料是有效或無效的。
assertValid
判斷 Response 中,不包含給定索引鍵的驗證錯誤。該方法可用來判斷以 JSON 結構回傳驗證錯誤,或是將驗證錯誤快閃存入 Session 的 Response:
1// 判斷無驗證錯誤訊息...2$response->assertValid();34// 判斷給定的索引鍵中沒有給定的驗證錯誤訊息...5$response->assertValid(['name', 'email']);
1// 判斷無驗證錯誤訊息...2$response->assertValid();34// 判斷給定的索引鍵中沒有給定的驗證錯誤訊息...5$response->assertValid(['name', 'email']);
assertInvalid
判斷 Response 中,給定的索引鍵是否有驗證錯誤訊息。該方法可用於檢查以 JSON 格式回傳錯誤訊息,或是將驗證錯誤訊息快閃存入 Session 的 Response:
1$response->assertInvalid(['name', 'email']);
1$response->assertInvalid(['name', 'email']);
也可以判斷給定的索引鍵是否有特定的錯誤訊息。在判斷是否有特定的錯誤訊息時,可提供完整的訊息,或是其中一段的錯誤訊息:
1$response->assertInvalid([2 'name' => 'The name field is required.',3 'email' => 'valid email address',4]);
1$response->assertInvalid([2 'name' => 'The name field is required.',3 'email' => 'valid email address',4]);